domingo, 26 de octubre de 2014

OpenCV. Primeros Pasos

Qué todavía no has escuchado de OpenCV??? Bien en parte es responsabilidad nuestra.

Y qué tal acerca del Kinect? No solo para jugar videojuegos sino por todos estos proyectos: Los de aquí y los de acá. La mayoría de estos proyectos que  son de desarrolladores independientes usan OpenCV y los proyectos que son de empresas grandes usan un software parecido.

OpenCV es una librería originalmente escrita para C/C++, pero actualmente también se la puede usar con los lenguajes Java, C# y Python, que son derivados de C++. Se puede usar en Windows, Linux, Mac Os, iOs y Android. (Es decir, en casi todo lo que tenga una pantalla).  Así es, no es más que un montón de código fuente que sirve para hacer algo de magia con imágenes y vídeos:
Nacido en los laboratorios de Intel en 1998, es ahora soportado principalmente por Itseez, donde continúan trabajando cuatro personas del equipo de desarrollo original.
Para usar su potencial se necesita descargar desde aquí. La última versión estable es la 2.4.10 pero la documentación para Python es para la versión alpha 3.0, sin embargo también es válida para la versión 2.4.10

Casi seguro ya tienes instalado Python en tu PC, pero por si no es así, basta con ir a: https://www.python.org/downloads/  para descargarlo. Descarguen la version 2.7 que es compatible con la versión 2.4.10 de OpenCV.
Para obtener OpenCV igualmente solo es necesario ir a Download opencv-2.4.10.exe (367.3 MB) si tienes Windows o ir a http://sourceforge.net/projects/opencvlibrary/ en caso de tener otro sistema operativo.
Además se necesita descargar e instalar Numpy (http://sourceforge.net/projects/numpy/)  y Matplot  (http://sourceforge.net/projects/matplotlib/).

Adermás si bien se puede usar varios editores de código, recomiendo PyCharm en su versión Community (https://www.jetbrains.com/pycharm/download/)

Con todos eso instalado podemos empezar a usar OpenCV. Por si falta algún paso pueden pasarse por aqui .


 Para probar que todo esta bien conecten su cámara web y poner esto dentro de un archivo y llamenlo pruebas.py o como deseen pero preferiblemente con la extensión ".py"

import numpy as np
import cv2
import cv2.cv as cv 
#Creamos un objeto que maneje la camara
cap = cv2.VideoCapture(0)
cap.open(0) 
#Repetimos hasta que se cierre el programa
while(True):
   #Capturamos la imagen actual 
   ret, img = cap.read()
   #Si la captura es correcta entonces la mostramos
   if ret:
       cv2.imshow('img',img)
   #Eperamos que  el usuario presione alguna tecla, en este caso la  letra "q" 
   # que nos servirá para cerrar el programa.
   key=cv2.waitKey(1)
   if  key == ord('q'):
       break
#Al salir del bucle infinito cerramos la ventana y dejamos de utilizar la cámara.
cap.release()
cv.DestroyWindow("img")

Recuerden que ne Python la indentación o tabulación es importante, al igual que los dos puntos al final de las instrucciones de ciclos o comparación como en este caso if y while.

Eso es todo. Pueden probar los ejemplos que vienen junto a OpenCV y también leer tutoriales en OpenCV-Python Tutorials o si gustan en español en Tutorial-OpenCV
Y claro no nos olvidemos de Youtube, fuente inagotable de ideas y proyectos realizados alrededor del mundo. 









miércoles, 15 de octubre de 2014

ML VIII: Modelos Ocultos de Markov.

Modelos Ocultos de Markov

Un proceso global puede representarse usando redes bayesianas como:
Incorporando los supuestos de Markov:
También podemos describir el proceso general mediante una cadena de Markov de variables latentes:
 Incorpora los supuestos de Markov:
Se aprecia que usando las cadenas de Markov tan solo tenemos las variables x, que son los estados ocultos, y las variables z, que son observables. (Saber más...)
Para comprender el por qué son útiles los Modelos Oculto de Markov o HMM (siglas en inglés, que en español queda MOM y tomando el significado en inglés de esta palabra no queda ninguna duda de su utilidad :-D) veamos el siguiente gráfico.  
Imaginad que cada cuadrado de color es una caja con caramelos de distintos sabores, en el tiempo n-2 podemos sacar un caramelo de cualquiera de las cajas. Claro, cada caja tiene cierta probabilidad de que obtengamos un sabor específico. Solo sabremos el sabor una vez que el caramelo sale de la caja.
Luego en el tiempo n-1 nos movemos a obtener el caramelo de otra caja o incluso de la misma caja. El movernos hacia determinada caja o quedarnos con la misma caja también tiene una probabilidad asociada, y al final no se conoce de que caja se saco el caramelo sino solo el sabor del caramelo.

Surgen entonces varias alternativas de incógnitas, mas los problemas básicos que se tratan de resolver son:
1. Calcular eficientemente la probabilidad P(Z/L) de la secuencia de observación Z (Ejemplo de Z: coco, piña, fresa) dado el modelo L y la secuencia de observación Z requerida. Donde el modelo L contiene tanto la matriz A de probabilidades de transición de una caja a otra, la probabilidades de obtener un sabor en cada caja y las probabilidades de estado inicial p  (Pues si nos comemos los caramelos y no los devolvemos a la cajas las probabilidades B cambiaran pero basándose en estas de estado inicial).
2. Encontrar la trayectoria mas probable X (las cajas de cada momento) dado L y la secuencia Z.
3. Ajustar los párametros A,B,p apara máximizar P(Z/L).

Además, con el propósito de resolver estos problemas existen asi mismo 3 operaciones a realizar en los HMM.
1. Calcular P(Z/L) dado un L conocido. Mediante el algoritmo de Forward-Backward
2. Calcular la secuencia óptima de estados X, con un L conocido. Se usa el algoritmo de Viterbi.
3. Aprender o encontrar el modelo L para un Z dado. Se emplea expectación-maximización o Baum-Welch.

Como se puede presumir todos los algoritmos mencionados anteriormente son ampliamente usados en inteligencia artificial. Pues son estos tres problemas básico y sus soluciones lo que nos permite predecir los estados para cualquier proceso de datos secuenciales.
Si cuantizamos (cuantificamos) el tiempo y los datos logramos dicha secuencialidad, y resulta que todo lo digital tiene justamente el tiempo y los datos ya cuantizados o cuantificados. En otras palabras si algo se puede digitalizar, es posible también usar inteligencia artificial, basada en todo lo que hemos visto hasta ahora, en ese algo específico.

Por último solo recordaremos que audio, video y texto han sido digitalizados desde antes que nacieras (si tienes menos de 20 años, claro). El tacto, ambiente (clima) y la orientación se han demorado un poco más pero ya poseen su formato digital también. El olfato y gusto tienen investigaciones en curso y tan solo faltaría lo emocional (que en buena parte esta en el texto y vídeo) para poder aplicar inteligencia artificial en casi todo ámbito humano.

domingo, 5 de octubre de 2014

ML VII Modelos gráficos probabilísticos, Redes Bayesianas

Bueno, hasta ahora hemos visto 101 diapositivas en 5 artículos, lo cual indica mas o menos 20 diapositivas por artículo, espero sus comentarios para saber si quieren más resumido o más extenso el contenido.

La probabilidad nos ha acompañado al largo de todo el curso mediante fórmulas pero una forma de representarla es también gráficamente.

Una gráfica de modelo probabilístico es una representación esquemática de una distribución de probabilidad. 
En un modelo gráfico, las variables aleatorias se representan como nodos y las dependencias estadísticas se representan utilizando los enlaces entre los nodos. 
El gráfico resultante puede tener las siguientes propiedades: 
• Cíclico / Acíclico 
• Dirigida / No Dirigido 
• Los gráficos más simples son los gráficos acíclicos dirigidos(DAG).

Ejemplo:
Dado 3 variables aleatorias, y su probabilidad conjunta: p(a,b,c)=p(c|a,b)p(a,b)=p(c|a,b)p(b|a)p(a)
Redes Bayesianas

Un modelo basado en DAG es una red bayesiana.
En general con K variables aleatorias se podría tener una distribución conjunta de probabilidad: 
p(x1,...,xK)=p(xK|x1,..xK-1)....p(x2|x1)p(x1)
Esto lleva a un gráfico completamente conectado. 
• Nota: El orden de los nodos en un gráfico totalmente conectado es arbitrario. 
Todos ellos representan la distribución de probabilidad conjunta:
p(a,b,c)=p(a|b,c)p(b|c)p(c)
p(a,b,c)=p(b|a,c)p(a|c)p(c)
...
etc
La independencia estadística se puede representar por la ausencia de bordes. Esto hace los cálculos eficientes.
Recordemos (son 4 meses de demora ;-D): Dos variables son independientes si  

 Y por tanto al usar el teorema de Bayes:

Además dos variables con condicionalmente independientes si:
 Que es equivalente a:
Cos estos conceptos en cuenta veamos unos ejemplos usando gráficos:

Ejemplo 1
El siguiente gráfico representa :

 Al marginalizar a c:
Se tiene que a y b no son independientes.

Si suponemos que se conoce c, se tiene lo siguiente:
Por tanto a y  b, son condicionalmente independientes con respecto a c, y se dice que c es un nodo cola a cola (tail-to-tail node) en el trayecto entre a y b 

 Ejemplo 2
El siguiente dibujo representa:
Al marginalizar a c:

 Se tiene que a y b no son independientes.

 Si suponemos que se conoce c, se tiene lo siguiente:

Por tanto de nuevo a y b son condicionalmente independientes con respecto a c. Y a c se le llama nodo de cabeSe tiene que a y b no son independientes.
Siguiendo el mismo procedimiento marginalizando c se tiene que a es independiente de b.
Si se concidera que se conoce c:
Se tiene que a y b no son condicionalmente independientes de c. Además a c se le conoce como nodo cabeza a cabeza (head-to-head node).

Con estos ejemplos es claro ver como con los enlaces entre nodos y la dirección entre ellos, además de saber si el nodo intermedio corresponde a una variable conocida o no, se puede saber si existe o no independencia entre las variables.






sábado, 13 de septiembre de 2014

ML VI: Procesamiento de Imágenes I

Imagen Digital.
Una imagen representada digitalmente es una matriz de tres dimensiones: ancho, alto y color. Si el color es solo uno, como en una imagen en escala de grises, es una matriz de solo dos dimensiones.
Como las imágenes solos se ven en las dos dimensiones, son estas las que más importan y a cada pedacito se le llama píxel, el cual nos da la información de cuanto color hay en ese espacio.

Filtro de Imágenes.
Una de las herramientas usadas para procesar las imágenes es reemplazar en cada píxel una contribución ponderada de si mismo y de sus vecinos, los coeficientes de esta ponderación se representan en una matriz por lo general de 3 por 3 elementos, el central es el pixel al que se le rremplazara su valor, esto se hace recorriendo la imagen y realizando dicho proceso en cada píxel.
Matriz de Ponderación
Así por ejemplo a partir de esta imagen:

Aplicando el filtro de la imagen anterior se tiene:
Lo cual resalta las texturas de la imagen.
Así mismo aplicando el filtro con distintos coeficientes se tiene:

Aclarecer:


 Detección de bordes horizontal:

Detección de bordes vertical:


Fitro de media o promedio:


 Oscurecer:

 Por último el programa de Scilab con el cual se obtuvieron las imágenes de este post.

//variables iniiciales
dirimagen="";

//Crear ventana
ventana = createWindow();
//evitar warnings, si no se ejecuta correctamente comentar esta linea
funcprot(0);
//setlookandfeel("it.unitn.ing.swing.plaf.macos.MacOSLookAndFeel");

//funcion del boton "Abrir"
function SeleccionarImagen (botonSeleccionar,txtDirectorio)
    dirimagen=uigetfile(["*.png*";"*.jpg";"*.bmp"]);
    imagen=imread(dirimagen);
    txtDirectorio.String=dirimagen;
    imshow(imagen)
endfunction

//funcion del boton "Filtrar"
function Filtrar (botonFiltrar)
    h1=eval(txt1.String)
    h2=eval(txt2.String)
    h3=eval(txt3.String)
    h4=eval(txt4.String)
    h5=eval(txt5.String)
    h6=eval(txt6.String)
    h7=eval(txt7.String)
    h8=eval(txt8.String)
    h9=eval(txt9.String)
    //vuelve a cargar el archivo para quitar cambios
    dirimagen=txtDirectorio.String;
    imagen=imread(dirimagen);
    //Crear el filtro de 3x3
    filtro=[h1 h2 h3; h4 h5 h6; h7 h8 h9]
    //aplicar el filtro a la imagen
    imf = imfilter(imagen, filtro);
    imshow(imf);
endfunction

//Tamaño de la ventana
ventana.axes_size = [600 300];

//Otras propiedades//////////////////
//ventana.dockable="on";
//ventana.menubar_visible = "on"
//ventana.default_axes = "off"
//ventana.infobar_visible = "off"
ventana.toolbar_visible = "on";
ventana.figure_name="Filtros Imagenes"

//Caja de texto para la direccion(path) de la imagen
txtDirectorio=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.0 0.9 0.7 0.1], "string", "lena512.bmp", "horizontalalignment", "center");

//Cajas de texto para los coeficientes del filtro
txt1=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.2 0.6 0.1 0.1], "string", "1", "horizontalalignment", "center");
txt2=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.3 0.6 0.1 0.1], "string", "2", "horizontalalignment", "center");
txt3=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.4 0.6 0.1 0.1], "string", "1", "horizontalalignment", "center");
txt4=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.2 0.5 0.1 0.1], "string", "0", "horizontalalignment", "center");
txt5=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.3 0.5 0.1 0.1], "string", "0", "horizontalalignment", "center");
txt6=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.4 0.5 0.1 0.1], "string", "0", "horizontalalignment", "center");
txt7=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.2 0.4 0.1 0.1], "string", "-1", "horizontalalignment", "center");
txt8=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.3 0.4 0.1 0.1], "string", "-2", "horizontalalignment", "center");
txt9=uicontrol(ventana, "style", "edit", "units", "normalized", "position", [0.4 0.4 0.1 0.1], "string", "-1", "horizontalalignment", "center");

//Boton Seleccionar
botonSeleccionar=uicontrol(ventana, "style", "pushbutton", "units", "normalized", "position", [0.7 0.9 0.3 0.1], "string","Seleccionar Imagen", "horizontalalignment", "center", "callback", "SeleccionarImagen(botonSeleccionar,txtDirectorio)");

//Boton Filtrar
botonFiltrar=uicontrol(ventana, "style", "pushbutton", "units", "normalized", "position", [0.6 0.4 0.3 0.3], "string","Filtrar Imagen", "horizontalalignment", "center", "callback", "Filtrar(botonFiltrar)");


Como punto extra vale mencionar que se usa el modulo ATOM SIVP, que se puede instalar desde el menú Aplicaciones.
Además se creo una interfaz gráfica, con lo cual es más fácil interactuar con el programa.



Para probar el programa pueden copiar el código anterior o descargar el script.
El enlace para descargar este programa "filtro_imagens.sci" y otros de artículos anteriores es el siguiente:


Si quieren conocer más sobre el procesamiento de imagenes y acorde con los que nos interesa les recomiendo el libro Visión por Computador  de José Francisco Vélez Serrano y otros autores.
También estas diapositivas, que son un resumen del mismo libro. 

lunes, 16 de junio de 2014

REDES NEURONALES - BACK PROPAGATION PERCEPTRON CON UNA CAPA OCULTA

Una de las bases de la inteligencia artificial es la neurona artificial. Las capas ocultas en una red neuronal permite resolver problemas no linealmente separables. A continuacion se muestra el esquema, codigo y programa en MATLAB para resolver la compuerta logica XOR a traves del entrenamiento por el metodo de BACK PROPAGATION de una red neuronal con una capa oculta.

Esta red neuronal se compone de dos entradas, una capa oculta con 2 neuronas y una salida en la ultima capa. El esquema de la red neuronal se muestra en la siguiente figura:


El programa calcula los pesos del esquema utiiliando el metodo de Back Propagation con el metodo de aprendizaje delta.

PERCEPTRON CON UNA CAPA OCULTA Back Propagation XOR


%BOTON ENTRENAR.
function btEntrenar_Callback(hObject, eventdata, handles)
    %Obtener Datos de Tabla
    datos=get(handles.Tabla,'Data');
    x1=datos(:,1);
    x2=datos(:,2);
    yd=datos(:,3);

    %Otener datos iniciales de pesos y bias
    w110= str2double(get(handles.w110,'String'));
    w120= str2double(get(handles.w120,'String'));
    w210= str2double(get(handles.w210,'String'));
    w220= str2double(get(handles.w220,'String'));
    v10= str2double(get(handles.v10,'String'));
    v20= str2double(get(handles.v20,'String'));
    u210= str2double(get(handles.u210,'String'));
    u220= str2double(get(handles.u220,'String'));
    u30= str2double(get(handles.u30,'String'));
    alfa= str2double(get(handles.alfa,'String'));
 
    %Otener datos de iteraciones y error
    iteracionmax = str2double(get(handles.Iteraciones,'String'));
    errormax = str2double(get(handles.Error,'String'));

    %ENTRENAMIENTO
    w11= w110;
    w12=w120;
    w21= w210;
    w22= w220;
    v1= v10;
    v2= v20;
    u21= u210;
    u22= u220;
    u3= u30;
    condicion = 0;
    iteracion = 0;
    error_Total = zeros(1,iteracionmax+1);
    while(condicion == 0)
        for j = 1 : 4
            %Calcular la yObtenida
            %Capa Entrada
            z1 = x1(j);
            a11 = z1;
            z2 = x2(j);
            a12 = z2;

            %Capa Oculta
            z3 = a11*w11+a12*w21 + u21;
            a21 = funcion_activ(z3);
            z4 = a11*w12+a12*w22 + u22;
            a22 = funcion_activ(z4);

            %Capa Salida
            z5 = a21*v1 + a22*v2 + u3;
            yObtenida = funcion_activ(z5);

            %Calculamos error yObtenida*(1-yObtenida)*
            err = (yd(j)- yObtenida);

            %Correccion pesos Capa 2-3
            z= v1*a21+v2*a22+u3;
            d3 = err*derivada_fun_act(z);
            v1 = v1 + alfa*d3*a21;
            v2 = v2 + alfa*d3*a22;
            u3 = u3 + alfa*d3;

            %Correccion pesos Capa 1-2
            d21 = derivada_fun_act(z3)*v1*d3;
            w11 = w11 + alfa*d21*a11;
            w21 = w21 + alfa*d21*a12;
            u21 = u21 + alfa*d21;

            d22 = derivada_fun_act(z4)*v2*d3;
            w12 = w12 + alfa*d22*a11;
            w22 = w22 + alfa*d22*a12;
            u22 = u22 + alfa*d22;

            %Contar error total
            error_Total(iteracion+1) = error_Total(iteracion+1)+abs(err);
        end
        iteracion = iteracion +1
        if(iteracion >= iteracionmax)
            condicion =1;
        end
        if (error_Total(iteracion) <= errormax)
            condicion =1;
        end
    end

    %Mostrar los Resultados
    set(handles.w11,'String',num2str(w11));
    set(handles.w12,'String',num2str(w12));
    set(handles.w21,'String',num2str(w21));
    set(handles.w22,'String',num2str(w22));
    set(handles.v1,'String',num2str(v1));
    set(handles.v2,'String',num2str(v2));
    set(handles.u21,'String',num2str(u21));
    set(handles.u22,'String',num2str(u22));
    set(handles.u3,'String',num2str(u3));

    %Dibujar el error
    axes(handles.axes1);
    x=0:1:iteracionmax;
    plot(x,error_Total,'color','r', 'LineWidth', 2);
end

function f = funcion_activ(z)
    %SIGNOIDE
    f = 1/(1+exp(-z));
end

function fp = derivada_fun_act(z)
    %SIGNOIDE
    fp = (1-funcion_activ(z))*funcion_activ(z);
end

domingo, 1 de junio de 2014

ML V: Regularización y Regresión Lineal Bayesiana

Regularización
En el artículo anterior obtuvimos la función de regresión entre dos variables, que es un modelo matemático para el conjunto de datos dados, sin embargo se puede encontrar parámetros muy grandes por lo que es necesario agregar un factor de regularización llegando a obtener el siguiente problema:

Cuya solución al aplicar el gradiente para minimizar el error es:

Ahora debemos encontrar el parámetro lambda adecuado, pero antes observemos como afecta la variación de este parámetro:
Ahora con el uso de probabilidades encontraremos cual es el lambda adecuado, en la siguiente gráfica se observa como y en x1 esta efectado por ruido gaussiano, tal que la probabilidad de que se mida el dato t en es punto es:
Que se lee como: probabilidad de que sea t dado x,w y sigma (desviación estándar)  es igual a una distribucion normal de la variable t con media en y(x,w), que es la función de regresión, y varianza igual a sigma al cuadrado

Recordando el planteamiento del problema de regresión donde teníamos de datos los puntos dados (x1,t1), (x2,t2), ..., (xN,tN). Asumiendo que las probabilidades en cada punto son independientes se tiene:

Ahora aplicaremos logaritmos para pasar de las multiplicaciones a una sumatoria
Ahora asumamos una distribución gaussiana prior, o anterior , con distinta varianza y media 0, y podemos obtener una probabilidad posterior, la cual debemos maximizar.

Usando Bayes tenemos:
Donde el denominador por ser una integral con respecto a w, termina por no depender de w. Por eso maximizamos solo el numerador. Para pasar de la multiplicación a una suma volvemos a usar el logaritmo obteniendo al final:
Que es similar a la primera ecuación de este artículo, por tanto para obtener la Estimación Máxima a Posteriori (MAP ) debemos hacer lambda igual a la división de las varianzas.

Regresión Lineal Bayesiana
Usando MAP podemos encontrar ya parámetros adecuado pero que pasa si los datos son secuenciales, debemos buscar la manera de encontrar la probabilidad de un nuevo punto dados los puntos antiguos, a esto se llama la distribución predictiva:

   Esto se debe aplicar recursivamente tantas veces como nuevos puntos aparezcan.
Veamos un ejemplo, supongamos que tenemos y=-0.3+0.5x, e inicialmente varianza1=0.4 y varianza2=0.5.
Al principio sin ningún punto observado se tiene a priori:
El espacio de Hough se obtiene de una transformada que permite conocer si existe lineas rectas en el espacio de datos.

Al conocer el primer dato se tiene:
Al conocer el segundo dato:
Y así se va acercando cada vez mas a medida que se toman más datos, así con 20 datos:


Obtenemos la distribución predictiva integrando sobre todos los posibles parámetros del modelo:

La probabilidad a posterior puede encontrarse:

Donde Prior cov: Covarianza de la probabilidad a priori y Prior mean: media de la probabilidad a priori.
Podemos realizar un programa pero es mejor observar los resultados gráficamente:



Al igual que en el ejemplo anterior al agregar mas datos el modelo se vuelve más cercano al esperado.

Si bien una imagen vale más que mil palabras, a veces es más esclarecedor un ejemplo con palabras:

Imaginen que les presentan una foto, antes de verla les han dicho que lo que hay en  la foto es un animal.
Una vez que la ven, observan que tiene cuatro patas, es peludo y posee cola.
Luego les presenta otra foto de un animal de la misma especie, misma raza, ahora relacionan la características del animal de la foto anterior con las del animal de la nueva foto, por ejemplo el pelaje es café y los ojos y nariz son negros en ambos pero el uno es más pequeño y posee pelaje más corto.
Luego les presenta unas foto de un animal de la misma especie, pero diferente raza, si bien ahora hay más diferencias las semejanzas siguen siendo más numerosas, como orejas largas y hocico un tanto alargado.
Luego de muchas fotos más pueden reconocer un perro, no sabíamos que era un perro al inicio, sin problema de otra foto de un animal de otra especie.

Es lo que hemos estado realizando con la regresión bayesiana, con cada nuevo dato tenemos un modelo más reducido y por tanto reconocemos o clasificamos mejor nuevos datos que obtengamos. 

Además y para terminar:
Un modelo que se ha encontrado con el uso de regresión puede ser evaluado de diferentes maneras.
Esto se puede utilizar para ajustar los parámetros del modelo como el uso de un conjunto de datos de validación
La Regresión Lineal Bayesiana opera en datos secuenciales y proporciona la distribución predictiva.
Cuando se utiliza priores gaussianos (y ruido gaussiano), todos los cálculos se pueden hacer analíticamente, como todas las probabilidades siguen siendo gaussianas