Aprendizaje de maquinas -- R -- Modelos Gráficos Probabilisticos.

Notas de clase sobre aprendizaje de maquinas usando R

Juan David Velásquez Henao
jdvelasq@unal.edu.co
Universidad Nacional de Colombia, Sede Medellín
Facultad de Minas
Medellín, Colombia

[Licencia]

[Readme]

Software utilizado.

Este es un documento interactivo escrito como un notebook de Jupyter, en el cual se presenta un tutorial sobre regresión logistica usando R en el contexto de aprendizaje de maquinas. Los notebooks de Jupyter permiten incoporar simultáneamente código, texto, gráficos y ecuaciones. El código presentado en este notebook puede ejecutarse en los sistemas operativos Linux y OS X.

Haga click aquí para obtener instrucciones detalladas sobre como instalar Jupyter en Windows y Mac OS X.

Haga clic [aquí] para ver la última versión de este documento en nbviewer.

Descargue la última versión de este documento a su disco duro; luego, carguelo y ejecutelo en línea en Try Jupyter!

Haga clic aquí para ver el tutorial de visualización y gráficas.

Contenido

Bibliografía.

Material complementario.

Webinar RStudio Getting your data into R


Introducción

Contenido

Los modelos gráficos probabilisticos son representaciones en forma de grafo que describen la relación de independencia condicional entre un conjunto de variables aleatorias. Estos modelos proporcionan un marco poderoso para modelar las interacciones complejas entre varias variables aleatorias Y han sido existosamente aplicados a diagnostico médico así como a segmentación de imágenes.

Estos modelos manejan dos conceptos principales de dos ramas de la matemática, :

  • Teoría de grafos.
  • Teoría de probabilidad.

Teoría de Grafos

Contenido

Esta sub-rama de la matemática estudia los objetos conocidos como grafos, los cuales consisten de dos conjuntos: vértices (o nodos) enumerados y las conexiones entre los vértices (o bordes). Por lo tanto, un grafo no es nada más que una descripción de puntos y conexiones entre ellos. Cada uno de estas conexiones pueden tener direcciones (flechas) que van desde el nodo fuente o cola hasta el vértice objetivo o cabeza. Cuando el grafo presenta direcciones se le denomina un grafo dirigido, de lo contrario se denomina un grafo no dirigido donde las conexiones se representan con lineas y no flechas.

Una forma común de describir un grafo como el anterior es a través de una matriz de adyacencia. Si tenemos $ V $ vértices en el grafo, una matriz de adyacencia es de dimensión $ V \times V $ cuyas entradas son cero si el vertice del número de la fila no está conectado con el vértice del número de la columna. Si existe una conexión, el valor es 1. Cuando se trata de grafos no dirigidos, la matriz siempre es simetrica.

Un vértice se denomina $ v_{i} $ mientras que un borde o conexión se denomina $ (v_{i},v_{j}) $ donde el vértice $ i $ es la fuente y el vértice $ j $ es el destino. Por lo tanto se dice que dos vertices están conectados si tienen un camino representado por una o varias conexiones entre los nodos que permitan llegar de $ i $ hasta $ j $. Por ejemplo, los nodos 2 y 6 están conectados ya que pasando a través del nodo 1 se pueden llegar. No obstante, note que del 6 al 2 no existe camino.

Cabe aclarar que no importa la posición de los puntos o conexiones, solo es relevante que las conexiones entre los nodos se encuentren correctas. Es por esto que para los dos grafos mostrados se pueden representar con la misma matriz de adyacencia.

Dado que para dos vertices cualesquiera pueden existir varios caminos, definimos la distancia entre dos nodos como el camino más corto entre ellos, esto es la longitud medida en nodos de dicho camino. Por lo tanto, la distancia entre el nodo 2 y 6, es igual a 1.

Adicionalmente un camino que empieza y termina en el mismo vértice se le conoce como un ciclo. Cuando un grafo no tiene un ciclo se le conoce como un grafo aciclico. Adicional, cuando tiene direcciones se le conoce como un grafo acíclico dirigido (DAG por sus siglas en inglés).

Muchos fenómenos reales pueden ser representandos como un grafo, ya sea las conexiones de amigos en facebook, o las redes de internet, de telecomunicaciones, de transporte, de electriciadad, etc.

Dentro de la aplicación de aprendizaje de maquinas, asumimos que los nodos son variables aleatorias y las conexiones son las dependencias entre ellas.

Teorema de Bayes

Contenido

Para explorar más las variables aleatorias, se hace una corta introdución al teorema de Bayes. Para esto suponga que hay dos eventos $ A $ y $ B $. Por ejemplo el evento $ A $ puede ser que un paciente tenga apendicitis y el evento $ B $ que dicho paciente tenga alto nivel de globulos blancos. Por lo tanto, se define la probabilidad condicional de $ A $ dado $ B $ como lq proabilidad de que el evento $ A $ d suceda dado que el evento $ B $ ha sucedido anteriormente.

Matemáticamente, se define la proabilidad condicional como la división entre la probabilidad de que los dos eventos ocurran divido por la probabilidad de que el evento B ocurra:

$$ P(A|B)= \frac{P(A \cap B)}{P(B)} $$

Note que si los eventos son independientes entre sí la ecuación es simplemente la probabilidad de $ A $, lo cual es consistente ya que la ocurrencia de $ B $ no influye en la probabilidad de $ B $.

$$ P(A|B)= \frac{P(A)·P(B)}{P(B)} = P(A) $$

Por lo tanto se puede reexpresar la probabilidad conjunta como:

$$ P(A \cap B) = P(A|B) · P(B) = P(B|A)·P(A) $$

Luego, se define el teorema de Bayes como:

$$ P(A|B)= \frac{P(B|A)·P(A)}{P(B)} = \frac{P(B|A)}{P(B)}·P(A)$$

La anterior ecuación representa la probabilidad posterior de $ A $ dado que ha ocurrido $ B $, donde $ P(A) $ es la probabilidad a priori de la ocurrencia de $ B $ y el coeficiente $ \frac{P(B|A)}{P(B)} $ es el ajuste de la nueva información del evento $ B $ una vez ha ocurrido.

Independencia Condicional

Contenido

Existen ocasiones cuando dos variables $A, B$ puede que no sean estadísticamente independientes en el principio, pero observando una tercera variable $C$, puede resultar en que se vuelvan independientes. Formalmente, se define como:

$$ P(A \cap B | C)=P(A|C) · P(B|C) $$

Por ejemplo, si $J$ representa la probabilidad de que se le ofrezca una oferta de trabajo en una compañía y $G$ la probabilidad de ser aceptado en un estudio de posgrado en cierta universidad, puede ser que estos dos eventos son independientes dado que existen varios factores que influyen en estos eventos (entrevistas, candidatos, etc). No obstante, ambos pueden depender en la variable $U$ representando el desempeño de la persona en sus estudios universitarios.

Por lo tanto, si se desconoce $U$ pero se sabe que la persona fue aceptada en el estudio de posgrado puede incrementar la creencia en la probabilidad de conseguir un trabajo, ya que que en cierto modo tuvo un buen desempeño en sus estudios de la universidad. Luego, los eventos $J$ y $G$ no son tan independientes del todo.

Redes Bayesianas

Contenido

Las redes bayesianas son un tipo de grafos dirigidos acíclicos donde usualmente se les denomina a los nodos fuente y destino como nodos padres e hijos (o descendientes), respectivamente. En caso que exista un camino entre $A$ y $B$, se dice que $B$ es descendiente directo de $A$ y, dado que el grafo no tiene ciclos, la relación es mutualmente exclusiva.

Por lo tanto, las redes bayesianas tienen una propiedad donde dado los nodos padres, cada nodo en la red es condicionalmente independiente de todos los otros nodos que no sean descendientes. A lo anterior, se le denomina la propiedad de Markov local, lo cual permite factorizar la probabilidad conjunta de todas las variables aleatorias tomando en cuenta todos los nodos del grafo.

Ilustrando, para las tres variables $G, J, U$ se puede expresar:

$$ P(G,J,U)=P(G|J,U)·P(J,U)=P(G|J,U)·P(J|U)·P(U) $$

Esta regla es general y se puede aplicar a más variables sin perdida de generalidad, lo que permite simplificar cálculos ya que no se considera todas las combinaciones posibles, sino unicamente las que especifica la fórmula.

Clasificador Bayesiano Ingenuo

Contenido

El primer modelo gráfico probabilistico es el Clasificador Bayesiando Ingenuo, el cual es un modelo DAG que contiene un nodo padre y una serie de nodos hijos representando variables aleatorias que son dependientes solo del nodo padre sin depedencia entre ellas.

Usualmente el nodo padre se le conoce como el nodo causal donde, por ejemplo, el nodo "Sentimiento" influye en el nodo "triste", "divertido", etc. Por lo tanto, para realizar estimaciones en el nodo padre a partir de observaciones de los nodos hijos (es decir, los nodos hijos son las variables predictoras y el nodo padre la variable respuesta) se puede reexpresar la probabilidad conjunta como:

$$ P(C|F_{1},...,F_{n}) = \frac{P(C)·P(F_{1},...,F_{n}|C)}{P(F_{1},...,F_{n})} $$

Por lo tanto,se asume independencia de la red:

$$ P(C|F_{1},...,F_{n}) = \frac{P(C)· P(F_{1}|C)·...·P(F_{n}|C)}{P(F_{1},...,F_{n})} $$

Luego, para realizar un clasificador usando esta fórmula, el objetivo es escoger la clase $C_{i}$ que maximice la probabilidad posterior de una clase dadas unas características $P(C_{i}|F_{1},...,F_{n})$. Dado que el denominador no es influenciado por la clase $C_{i}$, el problema se convierte en maximizar el numerador:

$$ Clasificar \hspace{0.25cm} C_{i}: \underset{c}{\arg\max} P(C) · \prod_{i=1}^{n}P(F_{i}|C) $$

A partir de los datos se puede estimar las probabilidades $ P(F_{i}|C_{j}) $ como las proporciones relativas de las observaciones de la clase $C_{j}$ que tienen diferente valor de la variable $F_{i}$. También se puede estimar $P(C_{j}$ como la proporcion relativa de las observaciones que están asiganas a la clase $C_{j}$. Estos son los estimadores de máxima verosimilitud.

Aplicación

Contenido

El análisis de sentimientos, la tarea es analizar un determinado fragmento de texto para determinar el sentimiento que está siendo expresado por el autor, ya sea en blogs, twitters, noticias, etc. determinando si la reaccion es positiva, neutral o negativa.

Abarcar este problema se puede realizar a través de los modelos gráficos probabilisticos, donde el sentimiento (positivo o negativo) es el nodo padre y los nodos hijos son las palabras particulares que se encuentren en el texto. Un supuesto importante en Naive Bayes es que la presencia de cada palabra importante es independiente de las otras, lo cual es algo irrealista. No obstante, el modelo se desempeña formidable.

Para ello se utiliza el dataset de crítica de películas con 25.000 datos de entrenamiento y otros 25.000 de validación. Se usará la librería tm de R para operaciones de minería de texto.


In [2]:
## Instale y cargue las siguientes librerias
library(tm)                                     
library(caret)
library("e1071")


Loading required package: NLP
Loading required package: lattice
Loading required package: ggplot2

Attaching package: ‘ggplot2’

The following object is masked from ‘package:NLP’:

    annotate


In [5]:
## Lectura de datos

url<-"http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz"      # Link al data set

download.file(url,                                                         # URL del dataset
              destfile="tmp.tar.gz")                                       # Archivo de destino

In [6]:
## Los datos descargados quedan en directorio y se descomprimen

untar("tmp.tar.gz")                                                        # Descompresión del archivo TAR

La carpeta que se descomprimió aclImdb tiene unas subcarpetas llamadas train y test cada una con los archivos .txt que contiene cada review de cada pelicula separados por negativos y positivos.


In [8]:
## Asignación de variables

path_to_neg_folder <- paste0(getwd(),                                      # Pegar directorio local
                             "/aclImdb/train/neg")                         # Direccion a los negativos
path_to_pos_folder <- paste0(getwd(),              
                             "/aclImdb/train/pos")                         # Direccion a los positivos
   
nb_pos             <- Corpus(                                              # Devuelve la colección de documentos
                            DirSource(path_to_pos_folder),                 # Direccion de los documentos
                            readerControl = list(language = "en"))         # Lenguaje de los docuemtos
nb_neg             <- Corpus(
                            DirSource(path_to_neg_folder), 
                            readerControl = list(language = "en"))

nb_all  <- c(nb_pos, nb_neg, recursive = T)                                 # Concatenar colecciones 

meta(nb_all[[1]])                                                           # Datos del primer archivo


Error in UseMethod("meta", x): no applicable method for 'meta' applied to an object of class "character"
Traceback:

1. meta(nb_all[[1]])

Observe que los archivos se encuentra de la forma _#pelicula_calificacion_ donde la calificación es una escala de 0 a 10, siendo 10 la mejor calificación. Ahora se crea un vector con los nombres de los archivos.


In [ ]:
## Crear vector de nombres

ids <- sapply( 1 : length(nb_all),                     # Vector de 1 hasta el numero de peliculas
              function(x) meta(nb_all[[x]], "id"))     # Extaer el ID
                  
head(ids)                                              # Primeros seis datos

Posteriormente se extrae la calificación de cada película con la función sub


In [ ]:
## Obtener las calificaciones de cada pelicula

scores <- as.numeric(sapply(ids,                                                     # Nombre de los archivos
                            function(x) sub("[0-9]+_([0-9]+)\\.txt", "\\1", x)))     # Expresión regular para encontrar la calificación
                                
scores <- factor(ifelse(scores >= 5, "positive", "negative"))                        # Forzar clase de positivo-negativo con limite de calificacion 5
                                
summary(scores)                                                                      # Resumen de las calificacion

Cuando se trabaja con minería de texto, usualemente uno asocia palabras a los sentimientos ya que si una critica presenta adjetivos como aburrido, cliché, horrible tiende asociarse con una mala crítica, mientras que palabras como Excelente, genial, divertida son tendientes a una buena crítica.

Por lo tanto, antes de empezar se realizan pasos de pre-procesamiento como convertir las palabras a minúsculas, remover puntuaciones, numeros o palabras muertas (the, of, on, over, etc.)


In [ ]:
## Pre-procesamiento de datos

nb_all <- tm_map(nb_all, 
                 content_transformer(removeNumbers))           # Remover los números

nb_all <- tm_map(nb_all, 
                 content_transformer(removePunctuation))       # Remover caracteres de puntuación

nb_all <- tm_map(nb_all, 
                 content_transformer(tolower))                 # Forzar a minúscula

nb_all <- tm_map(nb_all, 
                 content_transformer(removeWords),             # Remover palabras 
                 stopwords("english"))                         # Palabras muertas

nb_all <- tm_map(nb_all, 
                 content_transformer(stripWhitespace))         # Quitar espacios en blanco

Ahora se procede a estimar las características a través de una matriz de terminos documentales donde cada fila corresponde a un documento y las columnas son las palabras de dicho documento. Cada entrada es un 0 o 1 donde indica la falta o la presencia de dicha palabra en el documento, respectivamente.


In [ ]:
## Creación de matriz

nb_dtm <- DocumentTermMatrix(nb_all)

dim(nb_dtm)    # Dimensiones de la matriz

nb_dtm

La matriz tiene 118.235 columnas lo que indica que esas son las palabras distintas en todos los documentos. Adicional se observa que la matriz es sparse, un tipo especial de matriz que contiene una gran cantidad de 0. En este caso existen 2.493.738 de valores distintos de cero y 2.953.381.262 de campos iguales a 0. Una forma de reducir la dimensionalidad de esta matriz es quitar las columnas que tenegan mayor número de ceros, donde se establece un porcentaje de aceptación que en este caso será del 99%.


In [ ]:
## Reducción matriz 

nb_dtm <- removeSparseTerms(x = nb_dtm, 
                            sparse = 0.99)  # Porcentaje de aceptación

dim(nb_dtm)

Observe que se redujo a 1603 columnas. Luego, se fuerza la matriz a ser binaria.


In [ ]:
## Forzar a matriz de 0 y 1

nb_dtm <- weightBin(nb_dtm)

inspect(nb_dtm[10:16, 1:6])

Una vez la matriz como se necesita, se procede a realizar el modelo Naive Bayes, donde primero se crea los datos de entranmiento y validación.


In [ ]:
## Particionamos sets training y testing
set.seed(443452342)                                                    # Semilla aleatoria

nb_df              <- as.data.frame(as.matrix(nb_dtm))                 # Convertir a data.frame
 
nb_sampling_vector <- createDataPartition(scores,                      # Crear vector de muestreo
                                          p = 0.80,                    # Porcentaje de muestreo
                                          list = FALSE)                # Devolver lista es falso

nb_df_train        <- nb_df[nb_sampling_vector,]                       # Variables predictoras de entrenamiento
nb_df_test         <- nb_df[-nb_sampling_vector,]                      # Variables predictoras de validación

scores_train       <- scores[nb_sampling_vector]                       # Variable respuesta de entrenamiento
scores_test        <- scores[-nb_sampling_vector]                      # Variable respuesta de validación

Creación del modelo


In [ ]:
## Generar el clasificador Naive Bayes

nb_model <- naiveBayes(nb_df_train,         # Variables predictoras de entrenamiento
                       scores_train)        # Variable respuesta de entrenamiento

Predicción y porcentaje de ajuste


In [ ]:
## Predicciones y ajuste

nb_train_predictions <- predict(nb_model,        # Modelo de Naive Bayes
                                nb_df_train)     # Variables predictoras de entrenamiento

mean(nb_train_predictions == scores_train)       # Porcenjate de acierto

table(actual = scores_train,                     # Valores reales
      predictions = nb_train_predictions)        # Valores predecidos

Evaluación del modelo con los datos de validación


In [ ]:
## Predicciones y evaluación de precisión

nb_test_predictions <- predict(nb_model,         # Modelo de Naive Bayes
                               nb_df_test)       # Variables predictoras de validación

mean(nb_test_predictions == scores_test)         # Porcentaje de acierto

table(actual = scores_test,                      # Valores reales
      predictions = nb_test_predictions)         # Valores predecidos

Ejercicio.-- Utilice la base de datos de imagenes con letras escritas proporcionada por Kaggle para predecir que tipo de letra es utilizando modelo básico de Naive Bayes.

Datos


In [ ]:


Modelos de Markov Ocultos

Contenido

Los modelos de Markov ocultos (HMM) son redes bayesianas con una estructura repetitiva entre ciertos estados que se puede utilizar para predecir secuencias. El diagrama básico de cómo se ve un HMM es:

Al observar la secuencia avanza de izquierda a derecha donde cada nodo del camino contiene un par de nodos conectados. Los nodos $C_{i}$ son conocidos como estados latentes, escondidos o solo estados dificiles de observar, donde los nodos $O_{i}$ son los estados observados. Como las redes bayesianas, las observaciones son independientes entre ellas dado su estado correspondiente. En los nodos escondidos, se produce una observación o un símbolo que hacen parte de la sequencia.

Estos modelos son ampliamente usados en el procesamiento natural de lenguaje (NLP) en la tarea de obtener una parte del habla y sacar la sequencia de las "etiquetas" (Sustantivo, adjetivo, adverbio, etc). También se utilizan en reconocimiento de imagenes, de intrusos en una red, entre otros.

Usualmente, se tiene una coleccion de sequencias etiquetadas con su respectivo estado donde, análogamente al clasificador ingenuo Bayesiano, se usa los conteos de frecuencia relativos para estimar la probabilidad inicial del algortimo. Cuando no se tienen clasificados los datos, la tarea es más dificil ya que pueden ser varios estados y se tienen que probar las combinaciones posibles. Un posible método es através del algoritmo Baum-Welch.

Una vez se estiman los parámetros del modelo, la cuestión es cómo predecir la sequencia más probable de estados detrás de una secuencia de observaciones. A continuación, se hace una inroducción de este tipo de algoritmos.

Aplicación HMM

Contenido

Dentro del campo de la biología los modelos HMM son ampliamente usados para la prediccion de secuencias genéticas. El DNA se compone de cuatro moleculas fundamentales: Timinia, Citosina, Adenina y Guanina, donde el orden en que estas aparecen en la secuencia codifica la información genética que contiene el ADN.

Un problema interesante es encontrar una secuencia promotora que juegan un papel importante en regular la transcripción de genes. Para esta tarea, se utiliza el HMM que discrimine qué secuencias son promotoras y que otras no.


In [ ]:
## Descargue e instale las siguientes librerias
library("HMM")

In [ ]:
## Lectura de datos

link      <- paste0("https://archive.ics.uci.edu/ml/machine-learning-databases/",      # Link de los datos
             "molecular-biology/promoter-gene-sequences/promoters.data")

promoters <- read.csv(link,                                                 # Datos
                      header = F,                                           # Sin encabezado
                      dec = ",",                                            # Indicador decimal
                      strip.white = TRUE,                                   # Elimina espacios en blanco al principio
                      stringsAsFactors = FALSE)                             # Cadenas de texto no se forzan a ser factores

promoters[1,]                                                               # Primer registro de la secuencia

Note que la primera columna denota las cadenas promotoras o no (+ o -), por lo tanto se divide el conjunto de datos.


In [ ]:
## Separar cadenas promotoras

positive_observations <- subset(promoters,   # Data 
                                V1 == '+',   # Cadenas promotoras (+)
                                3)           # Seleccionamos la columna 3

negative_observations <- subset(promoters,   # Data
                                V1 == '-',   # Cadenas promotoras (+)
                                3)           # Seleccionamos la columna 3

Para el modelo HMM, es necesario concatenar todas las cadenas de la misma clase, pero sin perder el inicio y el fin de cada uno. Para ello se coloca una S al principio de cada cadena y una X al final.


In [ ]:
## Identificar el inicio y final de cada cadena

positive_observations <- sapply(positive_observations,                  # Cadenas
                                function(x) paste("S", x, "X", sep="")) # Pegar S al principio y X al final
                                    
negative_observations <- sapply(negative_observations,                  # Cadenas
                                function(x) paste("S", x, "X", sep="")) # Pegar S al principio y X al final
    
positive_observations[1]                                                # Primer dato

Ahora se divide la secuencia en cada una de sus moleculas con la función strsplit()


In [ ]:
## Separar las cadenas

positive_observations <- strsplit(positive_observations, "")      # Separar la cadena en cada molecula (letra)
negative_observations <- strsplit(negative_observations, "")      

head(positive_observations[[1]], n = 15)                          # Mostrar 15 primeros datos

Ahora que los datos están organizados, hay definir las matrices de probabilidad con los 6 estados que se presentan (4 moleculas más X y S).


In [ ]:
## Estimación de las matrices de probabilidad

states                          <- c("S", "X", "a", "c", "g", "t")     # Estados de la posición de la cadena
symbols                         <- c("S", "X", "a", "c", "g", "t")    # Simbolos de cada estado

startingProbabilities           <- c(1,0,0,0,0,0)       # Probabilidades iniciales
emissionProbabilities           <- diag(6)              # Matriz 6x6 con 1's en su diagonal

colnames(emissionProbabilities) <- states     # Asignamos nombre a las columnas
rownames(emissionProbabilities) <- symbols    # Asignamos nombre a las filas

emissionProbabilities                         # Mostrar la matriz

Definir la función calculateTransitionProbabilities que calcula la probabilidad de transición de un estado a otro.


In [ ]:
## Funcion para calcular probabilidades de transición

calculateTransitionProbabilities <- function(data, states) {
    
    # Crear matriz
             transitionProbabilities  <- matrix(0,                # Cero en las entradas
                                               length(states),    # numero de Filas
                                               length(states))    # Numero de columnas
    
    # Nombre de la filas y columnas 
    colnames(transitionProbabilities) <- states
    rownames(transitionProbabilities) <- states
    
    for (index in 1:(length(data) - 1)) {
        current_state <- data[index]                 # Estado actual
        next_state    <- data[index + 1]             # Proximo estado
        transitionProbabilities[current_state, next_state] <-transitionProbabilities[current_state, next_state] + 1 # Se hace el conteo de los passos de estado
    }
    
    # Extraer frencuencias relativas
    transitionProbabilities <- sweep(transitionProbabilities,              # Barrer por la matriz de ocurrencias
                                     1,                                    # Recorrer por filas
                                     rowSums(transitionProbabilities),     # Estadística de suma de las filas
                                     FUN = "/")                            # Dividir entre las filas
    
    return(transitionProbabilities)                                        # Devolver matriz
}

Dado que hay tan pocas observaciones, es recomendable usar validación cruzada dejando uno afuera para mejorar la estimación del modelo.


In [ ]:
## Validación cruzada dejando uno afuera y matrices

negative_observation        <-Reduce(function(x, y) c(x, y),     # Reducir la matriz
    negative_observations,                                       # Datos de entrenamiento
    c())
    
(transitionProbabilitiesNeg <- calculateTransitionProbabilities(negative_observation,  # Valores ajustados 
                                                                states))               # Valores de los estados

Inciamos el HMM con la función initHMM de la librería HMM.


In [ ]:
## Inicializar modelos HMM

negative_hmm <- initHMM(states,                                        # Estados
                        symbols,                                       # Símbolos
                        startProbs = startingProbabilities,            # Probabilidades iniciales
                        transProbs = transitionProbabilitiesNeg,       # Probabilidades de transición
                        emissionProbs = emissionProbabilities)         # Probabilidades de emision

Evaluación de las predicciones correctas.


In [ ]:
## Validación del ajuste
incorrect <- 0             # Inicializar vector incorrect

# Hacer un loop para contar los incorrectos
for (obs in 1 : length(positive_observations)) {
    positive_observation <- Reduce(function(x, y) c(x, y), positive_observations[-obs], c())               # Quitar una observación
        
    transitionProbabilitiesPos <- calculateTransitionProbabilities(positive_observation, states)           # Calcular las matrices de transición
        
    positive_hmm <- initHMM(states, symbols,startProbs = startingProbabilities,                            # Iniciar el modelo HMM
                            transProbs = transitionProbabilitiesPos,emissionProbs = emissionProbabilities)
        
    test_observation <- positive_observations[[obs]]               # Observaciones de test
        
    final_index     <- length(test_observation)                    # Longitud de cuántas observaciones
        
    pos_probs      <- exp(forward(positive_hmm, test_observation)) # Matriz con el logaritmo de la probabilidad.
    neg_probs      <- exp(forward(negative_hmm, test_observation)) # en cada paso
        
    pos_seq_prob   <- sum(pos_probs[, final_index])                # Sumar las cadenas positivas y negativas
    neg_seq_prob   <- sum(neg_probs[, final_index])
        
    if (pos_seq_prob < neg_seq_prob) {incorrect <- incorrect + 1}  # Contar el incorrecto
}

incorrect                                                          # Mostrar el número de incorrectos

In [ ]:
## Iniciar el modelo HMM para promotores negativos

positive_observation <- Reduce(function(x, y) c(x, y), positive_observations, c())
    
transitionProbabilitiesPos <- calculateTransitionProbabilities(positive_observation, states)
    
positive_hmm <- initHMM(states, 
                       symbols, 
                       startProbs = startingProbabilities, 
                       transProbs = transitionProbabilitiesPos,
                       emissionProbs = emissionProbabilities)

In [ ]:
for (obs in 1:length(negative_observations)) {
    negative_observation<-Reduce(function(x, y) c(x, y), negative_observations[-obs], c())
        
    transitionProbabilitiesNeg <-calculateTransitionProbabilities(negative_observation, states)
        
    negative_hmm     <- initHMM(states, 
                                symbols, 
                                startProbs = startingProbabilities,  
                                transProbs = transitionProbabilitiesNeg, 
                                emissionProbs = emissionProbabilities)
        
    test_observation <- negative_observations[[obs]]
        
    final_index      <- length(test_observation)
        
    pos_probs        <- exp(forward(positive_hmm,test_observation))
    neg_probs        <- exp(forward(negative_hmm,test_observation))
        
    pos_seq_prob     <- sum(pos_probs[, final_index])
    neg_seq_prob     <- sum(neg_probs[, final_index])
    if (pos_seq_prob > neg_seq_prob) incorrect <- incorrect+1
}
    
incorrect
    
(cross_validation_accuracy <- 1 - (incorrect/nrow(promoters)))

Ejercicio.-- Una firma inversora en bolsa necesita saber para un grupo de acciones en las cuales tiene invertida una gran cantidad de capital cuales son los momentos al alza (bull) o a la baja (bear) con el fin de planear sus estrategías de compra-venta así como identificar oportunidades de invertir.

Se debe realizar un modelo donde a partir de la entrada de una serie de tiempo financiera prediga si la acción va a subir o bajar en una determinada ventana de tiempo. Luego de un análisis de la literatura, encuentra que los modelos de HMM pueden predecir si una serie de tiempo subirá o caerá en un régimen de tiempo.

Para descargar las series de tiempo, utilice la librería Quandl. Adicionalmente, realice todos los supuestos que requiera, así como ventana de tiempo de la inversión o la acción con la cual va a trabajar.


In [ ]: