PRACTICA DE ENTRENAMIENTO DE REDES
NEURONALES
Miguel Chillitupa Carrasco
San Martin de Porres Lima, Perú
Resumen – el objetivo del desarrollo de esta práctica es el manejo de las 2.- Ejercicio 1, Comandos de Matlab:
principales herramientas de las redes neuronales usando para ello el a).- newff: es un comando que es utilizado para la creación de una red
software de matlab, demos (neural network toolbox) y funciones. neuronal (con una corrección de error por backpropagation) posee
diferentes sintaxis como por ejemplo los que presenta el help de
Breve descripción teórica: matlab:
Red neuronal artificial (RNA) son dispositivo o software programado de net = newff(P,T,S)
manera tal que tratan de representar al cerebro humano simulando su net = newff(P,T,S,TF,BTF,BLF,PF,IPF,OPF,DDF)
proceso de aprendizaje, el conocimiento es adquirido a travez de un
proceso de aprendizaje (entrenamiento), las interconexiones (pesos pero se elige la siguiente por ser la que más se usa dentro de este
sinápticos) entre las neuronas son usadas para almacenar el informe:
conocimiento. [1] net = newff(p,t,n,{},'traingd'); , donde:
El aprendizaje es el proceso por el cual una red neuronal modifica sus - p: datos de entrada, los cuales pueden ser un valor, vector,
pesos en respuesta a una información de entrada (en el proceso de matrices, etc. Definidos todos los valores o por rango de
aprendizaje los cambios se reducen a las destrucción, modificación y variación.
creación de conexiones entre las neuronas). - T: Salida real, valores de los cuales se sabe que son los
valores exactos de la función evaluada al ser evaluada en p.
1.- Ejercicio 1 - n: numero de neuronas dentro de la capa oculta de la red
La red neuronal en estudio es del tipo aprendizaje supervisado por ejemplo: [10 2 1] , 10 neuronas en la segunda capa, 2 en la
corrección de error y según la arquitectura pertenece a clase perceptron tercera capa, 1 en la capa de salida
multicapa (MLP), para el entrenamiento se utiliza la gradiente - {}: función de transferencia por default {}, otras pueden ser
descendiente, alimentando el error hacia atrás (backpropagation). logsig, purelin, etc.
- trained: El método de entrenamiento, ejemplo traingd que es
Fig.1. Red neuronal con el aprendizaje terminado, para un punto p=1.0, el método del gradiente
función evaluada: 𝒕 = 𝟏 + 𝐬𝐞𝐧𝐨(𝒑 ∗ 𝝅/𝟒), topología: 2 neuronas en - net: red neuronal creada
capa oculta , una neurona de salida, función de transferencia, logsig y
purelin respectivamente, error de salida cero, alcanzada en la la época Ejemplo: en la Fig.2, la red neuronal tiene una capa de entrada
17, se muestran los pesos, bias, y salidas parciales (a1, a2). también se constituida por 31 neuronas, una segunda capa formada por 10
aprecia en detalle las formulas usadas por matlab para el cálculo de estos neuronas, una tercera capa formada por 2 neuronas y la capa de salida
parámetros. constituida por una sola neurona.
Una forma de r epresentar de la red neuronal mediante matlab:
𝒏𝒆𝒕 = 𝑛𝑒𝑤𝑓𝑓([−1,1; −1,1; … ; −1,1], [10 2 1], {′ 𝑝𝑢𝑟𝑒𝑙𝑖𝑛′
′𝑝𝑢𝑟𝑒𝑙𝑖𝑛′ ′𝑝𝑢𝑟𝑒𝑙𝑖𝑛′}, ′𝑡𝑟𝑎𝑖𝑛𝑏𝑓𝑔′);
b).- traingd: Algoritmo de pasos descendientes, que actualiza pesos y
ganancias variándolos en la dirección negativa del gradiente de la
función del error
c).- train: es un comando que es utilizado para iniciar el entrenamiento,
Fig.1. sintaxis: .
[NET,TR] = train(NET,X,T)
Se muestra la tabla 1, donde se observa en detalle la evolución de los [NET,TR] = train(NET,X)
principales valores, durante el proceso de aprendizaje de la red neuronal, [NET,TR] = train(NET,X,T,Xi,Ai,EW)
separa en grupo de colores, simulacion (color verde), pesos y bias (color
naranja), y simulación y error (color verde).
Tabla 1., Evolución de los principales valores, durante el proceso de aprendizaje de la red neuronal
Época Primera capa Valor bias Backpropagate Simulación Segunda capa Valor Backpropagate Simulación t error
pesos W1 Salidas S1 Salidas a1 pesos W2 bia Salida S2 Salida a2
W1(1,1) W1(2,1) b1(1) b2(1,1) s1(1) s1(2) a1(1) a1(2) W2(1,1) W2(2,1) b2 S2 a2 t E
0 0.629 0.812 -0.746 0.827 -1.107 -0.254 0.609 0.529 0.265 -0.805 -0.443 -1.107 1.153 1.707 0.554
1 0.616 -0.691 -0.141 0.857 -0.109 -0.172 0.617 0.541 0.652 0.978 0.422 -0.708 1.353 1.707 0.354
2 0.627 -0.674 -0.130 0.874 -0.073 -0.112 0.622 0.550 0.695 1.016 0.493 -0.446 1.484 1.707 0.223
3 0.634 -0.662 -0.123 0.885 -0.047 -0.072 0.625 0.555 0.723 1.040 0.538 -0.279 1.568 1.707 0.140
4 0.639 -0.655 -0.118 0.892 -0.030 -0.045 0.627 0.559 0.741 1.056 0.565 -0.173 1.707 0.087
5 0.642 -0.651 -0.115 0.897 -0.019 -0.028 0.629 0.561 0.752 1.066 0.583 -0.107 1.653 1.707 0.054
6 0.644 -0.648 -0.113 0.900 -0.012 -0.018 0.630 0.563 0.758 1.072 0.594 -0.066 1.674 1.707 0.033
7 0.645 -0.646 -0.112 0.901 -0.007 -0.011 0.630 0.563 0.762 1.075 0.600 -0.041 1.687 1.707 0.020
8 0.646 -0.645 -0.111 0.903 -0.004 -0.007 0.630 0.564 0.765 1.078 0.604 -0.025 1.694 1.707 0.013
9 0.646 -0.644 -0.111 0.903 -0.003 -0.004 0.631 0.564 0.767 1.079 0.607 -0.016 1.699 1.707 0.008
10 0.646 -0.644 -0.111 0.904 -0.002 -0.003 0.631 0.565 0.768 1.080 0.608 -0.010 1.702 1.707 0.005
11 0.647 -0.644 -0.110 0.904 -0.001 -0.002 0.631 0.565 0.768 1.080 0.609 -0.006 1.704 1.707 0.003
12 0.647 -0.644 -0.110 0.904 -0.001 -0.001 0.631 0.565 0.769 1.081 0.610 -0.004 1.705 1.707 0.002
13 0.647 -0.644 -0.110 0.904 -0.000 -0.001 0.631 0.565 0.769 1.081 0.610 -0.002 1.706 1.707 0.001
14 0.647 -0.643 -0.110 0.904 -0.000 -0.000 0.631 0.565 0.769 1.081 0.611 -0.001 1.706 1.707 0.001
15 0.647 -0.643 -0.110 0.904 -0.000 -0.000 0.631 0.565 0.769 1.081 0.611 -0.001 1.707 1.707 0.000
16 0.647 -0.643 -0.110 0.904 -0.000 -0.000 0.631 0.565 0.769 1.081 0.611 -0.001 1.707 1.707 0.000
17 0.647 -0.643 -0.110 0.904 -0.000 -0.000 0.631 0.565 0.769 1.081 0.611 -0.000 1.707 1.707 0.000
18 0.647 -0.643 -0.110 0.904 -0.000 -0.000 0.631 0.565 0.769 1.081 0.611 -0.000 1.707 1.707 0.000
Nota: Convergencia al error cero en la época 17, (s1 y s2 también son cero)
Los valores asociados a train son:
- net.trainParam.epochs: Máximo número de iteraciones para
lograr la convergencia de la red neuronal
- net.trainParam.goal: Error máximo permitido
- net.trainParam.lr: Rata de aprendizaje, parámetro para la
modificación de la velocidad de cambio del error
- net.trainParam.time: Máximo tiempo de entrenamiento en
segundos
Ejemplo
% Ejemplo obtenido de matlab (>> help train)
% se han conservado los comentarios originales
clear all
p = [-1 -1 2 2; 0 5 0 5];
t = [-1 -1 1 1];
%Create the feedforward network.
net = newff(p,t,3,{},'traingd');
%In this simple example, turn off a feature that is
introduced later.
net.divideFcn = '';
%At this point, you might want to modify some of the
default training parameters.
net.trainParam.show = 50;
net.trainParam.lr = 0.05;
net.trainParam.epochs = 300;
net.trainParam.goal = 1e-5;
%If you want to use the default training parameters,
the preceding commands are not necessary.
%Now you are ready to train the network.
[net,tr] = train(net,p,t);
%The training record tr contains information about
the progress of training.
%Now you can simulate the trained network to obtain
Resultado del entrenamiento de la red neuronal, x (verde) respuesta de
its response to the inputs in the training set.
a = sim(net,p) la red con 1 epoca de entrenamiento, + (rojo) respuesta de la red al
termino del entrenamiento con un error de 0.01
2.- Ejercicio parte b
Entrenamiento de una RNA para mapeo de un conjunto de datos.
%Vectores de entrada (p) y salida (t) conocidas
p =[0 1 2 3 4 5 6 7 8];
t =[0 0.84 0.91 0.14 -0.77 -0.96 -0.28 0.66 0.99];
plot(p,t,'o')
net = newff([0 8],[10 1],{'tansig' 'purelin'}
,'trainlm');
%Creación de nuestra red neuronal
%[0 8], limites del vector de entrada
%[10 1], 10 neuronas en la capa oculta, 1 neurona en
la capa de salida
%'tansig': función de transferencia de la capa oculta
%'purelin': función de transferencia de la capa de
salida
y1 = sim(net,p);
%evaluación de la red generada (net) con los valores
del vector de entrada (p)
plot(p,t,'o',p,y1,'x')
%grafica de la red neuronal, con entrenamiento de 1
epoca
net.trainParam.epochs = 50;
net.trainParam.goal = 0.01;
%preparación para el uso de la función entrenador
(train), epochs=50 (epocas), goal=0.01 error máximo a
considerar
net = train(net,p,t);
%función de entrenamiento, utiliza la red neuronal
creada (net), el vector de entrada (p) y su relacionado
vector de salida (t)
y2 = sim(net,p);
%evaluación de la red generada (net) con los valores
del vector de entrada (p)
plot(p,t,'o',p,y1,'x',p,y2,'*')
%grafica de la red neuronal, con entrenamiento de 50
epocas (…epochs=50)
2.- Ejercicio parte c 2.- Ejercicio parte d
Entrenamiento de una RNA para el mapeo de la función seno. Entrenamiento de una RNA para mapeo de la función f(x) = 1/x
Ya que los comandos son muy similares a los usados para la red neuronal
del ejercicio anterior, algunos comentarios se obvian. clear all; clc;
p_treino = 1+99*(rand(70,1)');
clear all; %aqui hay una diferencia, no es 100% seguro que
clc; p_treine tomo el valor de 100=(1+99*1)
p = [0: 0.1: 6.3]; t_treino = 1./p_treino;
t = sin(p); plot(p_treino, t_treino, 'o');
plot(p,t,'o'); p_teste = 1+99*(rand(30,1)');
net = newff([0 6.3],[4 1],{'tansig' 'purelin'} t_teste = 1./p_teste; % valor desejado usado no teste
,'traingdx'); net = newff([1 100],[8 1],{'tansig' 'purelin'}
% [0 6.3]:Limites del vector de entrada ,'trainlm');
% [4 1] :4 neuronas en la capa oculta, una neurona en % [1 100]:Limites del vector de entrada, como se
la capa de salida mensiono en la creación de p_treino no necesariamente
%'tansig': función de transferencia de la capa oculta tendrá el valor de 100, pero para lacracion de nuestra
%'purelin': función de transferencia de la capa de red neuronal se toma este limite superior
salida % [8 1] :8 neuronas en la capa oculta, una neurona
%'traingdx': función de entrenamiento en la capa de salida
y1 = sim(net,p); %'tansig':función de transferencia de la capa oculta
plot(p,t,'o',p,y1,'x'); %'purelin': función de transferencia de la capa de
net.trainParam.epochs = 2500; salida
net.trainParam.goal = 0.001; % error máximo esperado %'trainlm': función de entrenamiento
net = train(net,p,t); net.trainParam.epochs = 800;
y2 = sim(net,p); net.trainParam.goal = 1e-8;
plot(p,t,'o',p,y1,'x',p,y2,'*'); net = train(net,p_treino,t_treino);
y_treino = sim(net, p_treino);
y_teste = sim(net, p_teste);
Estructura de la red neuronal de trabajo: subplot(2,1,1), plot(p_treino, t_treino,'o',
4 neuronas en la capa oculta p_treino, y_treino,'*');
1 neurona en la capa de salida subplot(2,1,2), plot(p_teste, t_teste,'o', p_teste,
y_teste,'*');
Estructura de la red neuronal de trabajo:
8 neuronas en la capa oculta
1 neurona en la capa de salida
Resultado del entrenamiento de la red neuronal, para un error de 0.001.
Se aprecia con o el vector de salida real,
Se aprecia con x el vector de salida en la red neuronal con entrenamiento
de 1 epoca
Se aprecia con * el vector de salida en la red neuronal con entrenamiento
de máximo 2500 epocas (se aprecia según la simulación que esto se Resultado del entrenamiento de la red neuronal, con error de 1e-8.
realiza al culminar la 415va época) Se aprecia con o el vector de salida real,
El resultado de en qué época la red neuronal converge variara según los Donde * el vector de salida en la red neuronal con entrenamiento de
pesos y bias escogidos al inicio. 1 epoca
Donde o el vector de salida en la red neuronal con entrenamiento de
máximo 2500 epocas (se aprecia según la simulación que esto se
realiza al culminar la 415va época)
3.- Ejercicio Resultados obtenidos: Primera Prueba
Hacer un programa computacional (similar al que se utilizó en las Entrenamiento usado: traingd
prácticas de laboratorio) para entrenar una RNA backpropagation de una Topologia: [20 2 1], capas ocultas y salida
camada escondida para realizar el mapeo de la siguiente función no Error: 0.001
lineal. Use las funciones del toolbox de RNA del Matlab. 1.- Topología de la red neuronal:
𝟑 2.- Valores alcanzados
𝒇(𝒙𝟏, 𝒙𝟐) = 𝐬𝐞𝐧(𝒙𝟏) ∗ 𝒄𝒐𝒔 ( ∗ 𝒙𝟐) , 𝒙𝟏 = 𝒙𝟐 = (−𝟑. 𝟓: 𝟎. 𝟐: 𝟑. 𝟓),
𝟒 3.- Graficas
A continuación se presenta el programa desarrollado en matlab, se
presenta un comentario resumido de lo realizado: De los resultados obtenidos se aprecia:
%-------- Vector de entrada para el entrenamiento No se logró llegar al mínimo error de 0.001 en las 2000 epocas (se
x1= [-3.5:0.01:3.5];
x2=x1;
obtuvo en este caso un error de 0.00618)
x_e=[x1;x2]; % Expresion en forma matricial
%-------- Vector de salida real para el entrenamiento
t_e = sin(x1).*cos(3/4*x2);
subplot(2,1,1), plot3(x1,x2,t_e, 'r');
title('Entrenamiento de red neuronal');
xlabel('Vector de entrada x1');
ylabel('Vector de entrada x2');
zlabel('Vector de salida');
grid;
%-------- Vector de entrada de prueba (despues de
entrenada la red
%-------- se probara la red con estos datos)
x1_p = [-5:0.5:5];
x2_p = x1_p;
x_p=[x1_p;x2_p];
%-------- Vector de salida real para la prueba
t_p = sin(x1_p).*cos(3/4*x2_p);
%-------- Creación de la red neuronal
net = newff([-3.5 3.5;-3.5 3.5],[20 2 1],{'tansig'
'purelin' 'purelin'},'trainlm');
%'tansig': función de transferencia de 1ra capa
oculta
%'purelin':función de transferencia de 2da oculta
%'purelin': función de transferencia de la capa de
salida
%'traingd': función de entrenamiento De las gráficas se observa, la relación entre la salida real y la salida de
net.divideFcn = ''; la red entrenada son muy parecidas, pero al realizar la prueba con un
net.trainParam.show = 50; segundo vector de entrada (diferente al que se usó en el entrenamiento)
net.trainParam.lr = 0.01; %parametro para manejar la la diferencia es apreciable en ciertos tramos.
velocidad de convergencia
net.trainParam.epochs = 2000; %numero de epocas
net.trainParam.goal = 0.001; %error permitido
net = train(net,(num2cell(x_e,1)),(num2cell(t_e,1)));
y_e = sim(net,(num2cell(x_e,1)));%La respuesta de la
red neural con la señal de entrenamiento
y_p = sim(net,(num2cell(x_p,1)));%La respuesta de la
red neural con la señal de prueba (comprobacion)
%-- Se convierte a y_e, del formato celula al formato
numero para graficar
Ne=length(y_e);
for i=1:Ne
a(i)=y_e{1,i};
end
%-- Se convierte a y_p, del formato celula al
formato numero para graficar
Np=length(y_p);
for i=1:Np
b(i)=y_p{1,i};
end
%-- Graficas respectivas de Entrada Vs Salida con la
señal entrenada
hold on
subplot(2,1,1),plot3(x_e(1,:),x_e(2,:),a,'g');
hold off
%-- Graficas respectivas de Entrada Vs Salida con la
señal de prueba
subplot(2,1,2),plot3(x_p(1,:),x_p(2,:),t_p,'y');
title('Prueba de la red neuronal entrenada');
xlabel('Vector de entrada x1');
ylabel('Vector de entrada x2');
zlabel('Vector de salida');
grid;
hold on
subplot(2,1,2),plot3(x_p(1,:),x_p(2,:),b,'b');
hold off
Resultados obtenidos: Segunda prueba Resultados obtenidos: Tercera prueba
Topologia: [20 2 1], capas ocultas y salida Entrenamiento usado: trainlm
Error: 0.001 Topologia: [20 2 1], capas ocultas y salida
1.- Topología de la red neuronal: Error: 0.00001
2.- Valores alcanzados 1.- Topología de la red neuronal:
3.- Graficas 2.- Valores alcanzados
3.- Graficas
De los resultados obtenidos se aprecia:
Se logró llegar al mínimo error de 0.001 en 12 epocas, una gran De los resultados obtenidos se aprecia:
diferencia entre el método de entrenamiento de la primera prueba Se logró llegar al mínimo error de 0.00001 en 54 epocas,
traingd y el usado en esta segunda prueba trainlm definitivamente el método de entrenamiento trainlm para este caso es
bueno.
De las gráficas se observa, la relación entre la salida real y la salida de
la red entrenada son muy parecidas, y al realizar la prueba con un
De las gráficas se observa, la relación entre la salida real y la salida de segundo vector de entrada (diferente al que se usó en el entrenamiento)
la red entrenada son muy parecidas, pero al realizar la prueba con un la diferencia es pequeña, el sistema mejoro
segundo vector de entrada (diferente al que se usó en el entrenamiento)
la diferencia es apreciable en ciertos tramos, similar al realizado en la
primera prueba
Resultados obtenidos: Cuarta prueba - Final Topologia de nuestra red, algoritmos usados, y evolución y
En base a los resultados obtenidos anteriormente y después de diversas resultados finales,
pruebas en diferentes topologías, a continuación se presenta el programa Entrenamiento usado: trainlm
desarrollado en matlab, con un comentario: Topologia: [10 1], capa oculta y salida
1.- Programa realizado Error: 0.00001
2.- Topología de la red neuronal:
3.- Valores alcanzados Topologia de la red: 2 entradas, 10 neuronas en la capa oculta, y una
4.- Graficas neurona de salida, función de transferencia tansing para la capa oculta
5.- Performance, evolución del entrenamiento, y regresión lineal y purelin para la salida. epocas: para este caso en particular 21 en total
clear all;
%-------- Vector de entrada para el entrenamiento
x1= [-3.5:0.1:3.5]; %Importante datos múltiplo de 0.1
x2=x1; %para el entrenamiento
x_e=[x1;x2]; % Expresion en forma matricial
%-------- Vector de salida real para el entrenamiento
t_e = sin(x1).*cos(3/4*x2);
subplot(2,1,1), plot3(x1,x2,t_e, 'r');
title('Entrenamiento de red neuronal');
xlabel('Vector de entrada x1');
ylabel('Vector de entrada x2');
zlabel('Vector de salida');
grid;
%-------- Vector de entrada de prueba (despues de
entrenada la red
%-------- se probara la red con estos datos)
x1_p=[-3.5:0.17:3.5];%Importante datos múltiplo 0.17
x2=x1; %para la comprobación
x2_p = x1_p;
x_p=[x1_p;x2_p];
%-------- Vector de salida real para la prueba
t_p = sin(x1_p).*cos(3/4*x2_p);
%-------- Creación de la red neuronal
net = newff([-3.5 3.5;-3.5 3.5],[10 1],{'tansig'
'purelin'},'trainlm');
%'tansig': función de transferencia de 1ra capa
oculta
%'purelin':función de transferencia de 2da oculta
%'trainlm': función de entrenamiento Grafica,
net.divideFcn = ''; 1) comparación de la señal de salida real (en color rojo) y la señal
net.trainParam.show = 5;
net.trainParam.lr = 0.001; %parametro para manejar la
obtenida al término del entrenamiento (verde).
velocidad de convergencia 2) salida real de comprobación nuevos datos múltiplos 0.17 (amarillo)
net.trainParam.epochs = 50; %numero de epocas y la señal obtenida de la red ya entrenada (azul) (superpuestas)
net.trainParam.goal = 0.00001; %error permitido
net = train(net,(num2cell(x_e,1)),(num2cell(t_e,1)));
y_e = sim(net,(num2cell(x_e,1)));%La respuesta de la
red neural con la señal de entrenamiento
y_p = sim(net,(num2cell(x_p,1)));%La respuesta de la
red neural con la señal de prueba (comprobacion)
%-- Se convierte a y_e, del formato celula al formato
numero para graficar
Ne=length(y_e);
for i=1:Ne
a(i)=y_e{1,i};
end
%-- Se convierte a y_p, del formato celula al
formato numero para graficar
Np=length(y_p);
for i=1:Np
b(i)=y_p{1,i};
end
%-- Graficas respectivas de Entrada Vs Salida con la
señal entrenada
hold on
subplot(2,1,1),plot3(x_e(1,:),x_e(2,:),a,'g');
hold off
%-- Graficas respectivas de Entrada Vs Salida con la
señal de prueba
subplot(2,1,2),plot3(x_p(1,:),x_p(2,:),t_p,'y');
title('Prueba de la red neuronal entrenada');
xlabel('Vector de entrada x1');
ylabel('Vector de entrada x2');
zlabel('Vector de salida');
grid;
hold on
subplot(2,1,2),plot3(x_p(1,:),x_p(2,:),b,'b');
hold off
Performance Conclusiones:
Son muy imporantes las siguientes selecciones:
- Considerando una red neuronal fija en su topologia, el
resultado de simular en qué época la red neuronal converge
variara, esto según los pesos y bias que se tengan al inicio
(ya que estos son usualmente números randonicos).
- Para la función de transferencia, en la capa oculta se escogio,
tansig esta elección se realizó debido a la naturaleza de la
señal de salida que tiene dos características importantes, la
primera es la variación entre numero positivos y negativos y
la segunda su naturaleza no lineal. En comparación con
logsig esta solo cumpliría la característica no lineal pero está
definida para señales negativas.
- Para la función de transferencia, en la capa salida se escogio,
purelin de similar forma se aprovechó la característica lineal
y el que está definida para rangos positivos y negativos.
- La elección del método de entrenamiento trainlm se realizó
por una conclusión del trabajo “diseño y simulación de una
red neuronal en vhdl y su aplicación en filtrado de un
electrocardiograma” [2], en el cual compara varios
métodos (funciones) de entrenamiento donde trainlm
(técnica Levenberg-Marquardt) destaca con un tiempo
menor de operaciónes y menor número de iteraciones
(epocas).
- El número de neuronas escogidas para el 3.- ejercicio, cuarta
prueba, (10 neuronas), se escogió después de varias pruebas,
donde este número y con un máximo de épocas de 50, y en
la topología de red escogida, llega en las numerosas pruebas
realizadas a converger al mínimo error pedido de 0.00001.
Bibliografia
[1] Notas de clase.
[2] Aguirre Miguel, Franco Zulay y Pateti Antonio, “diseño y simulación de
una red neuronal en vhdl y su aplicación en filtrado de un
electrocardiograma”.
Fuentes:
[2] Internet
[3] Matlab