TAREA 1

Cambiamos el corpus de entrenamiento y le asignamos sus nuevas categorías

document <- c(
  "una experiencia gastronomica inolvidable y un muy buen servicio",
  "el restaurante supero nuestras expectativas",
  "el ambiente ha sido relajado y perfecto y el personal fue muy amable",
  "el servicio era inexperto y no sabia como atender una mesa",
  "las raciones eran ridiculamente pequenas para el precio pagado", 
  "el ambiente era ruidoso y desagradable")

category <- c("+", "+", "+",  "-", "-", "-")

training <- data.frame(category, document)
training$document <- gsub(",", "", training$document)

cat("Corpus de entrenamiento:\n")
## Corpus de entrenamiento:
training
##   category                                                             document
## 1        +      una experiencia gastronomica inolvidable y un muy buen servicio
## 2        +                          el restaurante supero nuestras expectativas
## 3        + el ambiente ha sido relajado y perfecto y el personal fue muy amable
## 4        -           el servicio era inexperto y no sabia como atender una mesa
## 5        -       las raciones eran ridiculamente pequenas para el precio pagado
## 6        -                               el ambiente era ruidoso y desagradable

Cambiamos la frase a predecir

test <- "el servicio fue perfecto, con un camarero atento y amable"
test <- gsub(",", "", test)

cat("\n\nFrase a predecir:\n")
## 
## 
## Frase a predecir:
test
## [1] "el servicio fue perfecto con un camarero atento y amable"

Calculamos la bolsa de palabras

get_bow <- function(documents){
  bow <- table(unlist(strsplit(documents, " ")))
  bow <- as.data.frame(bow)
  names(bow)[1] <- "Word"
  bow <- bow[sort.int(bow$Freq, 
                      decreasing = TRUE,
                      index.return = TRUE)$ix, ]
  rownames(bow) <- NULL
  return(bow)
}
bow <- get_bow(training$document)
cat("\n\nBolsa de palabras:\n")
## 
## 
## Bolsa de palabras:
bow
##             Word Freq
## 1             el    6
## 2              y    5
## 3       ambiente    2
## 4            era    2
## 5            muy    2
## 6       servicio    2
## 7            una    2
## 8         amable    1
## 9        atender    1
## 10          buen    1
## 11          como    1
## 12  desagradable    1
## 13          eran    1
## 14  expectativas    1
## 15   experiencia    1
## 16           fue    1
## 17  gastronomica    1
## 18            ha    1
## 19     inexperto    1
## 20   inolvidable    1
## 21           las    1
## 22          mesa    1
## 23            no    1
## 24      nuestras    1
## 25        pagado    1
## 26          para    1
## 27      pequenas    1
## 28      perfecto    1
## 29      personal    1
## 30        precio    1
## 31      raciones    1
## 32      relajado    1
## 33   restaurante    1
## 34 ridiculamente    1
## 35       ruidoso    1
## 36         sabia    1
## 37          sido    1
## 38        supero    1
## 39            un    1

Calculamos la bolsa de palabras de cada categoría

bow_neg <- get_bow(training$document[training$category == "-"])
bow_pos <- get_bow(training$document[training$category == "+"])

Total de palabras

n_V <- nrow(bow)
n_neg <- sum(bow_neg$Freq)
n_pos <- sum(bow_pos$Freq)

Añadimos la probabilidad a ambas tablas

bow_neg$likel <- (bow_neg$Freq + 1)/(n_neg + n_V)
bow_pos$likel <- (bow_pos$Freq + 1)/(n_pos + n_V)

cat("\n\nBolsa de palabras negativas:\n")
## 
## 
## Bolsa de palabras negativas:
bow_neg
##             Word Freq      likel
## 1             el    3 0.06153846
## 2            era    2 0.04615385
## 3              y    2 0.04615385
## 4       ambiente    1 0.03076923
## 5        atender    1 0.03076923
## 6           como    1 0.03076923
## 7   desagradable    1 0.03076923
## 8           eran    1 0.03076923
## 9      inexperto    1 0.03076923
## 10           las    1 0.03076923
## 11          mesa    1 0.03076923
## 12            no    1 0.03076923
## 13        pagado    1 0.03076923
## 14          para    1 0.03076923
## 15      pequenas    1 0.03076923
## 16        precio    1 0.03076923
## 17      raciones    1 0.03076923
## 18 ridiculamente    1 0.03076923
## 19       ruidoso    1 0.03076923
## 20         sabia    1 0.03076923
## 21      servicio    1 0.03076923
## 22           una    1 0.03076923
cat("\n\nBolsa de palabras positivas:\n")
## 
## 
## Bolsa de palabras positivas:
bow_pos
##            Word Freq      likel
## 1            el    3 0.06060606
## 2             y    3 0.06060606
## 3           muy    2 0.04545455
## 4        amable    1 0.03030303
## 5      ambiente    1 0.03030303
## 6          buen    1 0.03030303
## 7  expectativas    1 0.03030303
## 8   experiencia    1 0.03030303
## 9           fue    1 0.03030303
## 10 gastronomica    1 0.03030303
## 11           ha    1 0.03030303
## 12  inolvidable    1 0.03030303
## 13     nuestras    1 0.03030303
## 14     perfecto    1 0.03030303
## 15     personal    1 0.03030303
## 16     relajado    1 0.03030303
## 17  restaurante    1 0.03030303
## 18     servicio    1 0.03030303
## 19         sido    1 0.03030303
## 20       supero    1 0.03030303
## 21           un    1 0.03030303
## 22          una    1 0.03030303

Calculamos la probabilidades a priori de cada categoría

prior_neg <- mean(training$category == "-")
prior_pos <- mean(training$category == "+")

cat("\n\nProbabilidad a priori de la categoría negativa:", prior_neg)
## 
## 
## Probabilidad a priori de la categoría negativa: 0.5
cat("\nProbabilidad a priori de la categoría positiva:", prior_pos, "\n")
## 
## Probabilidad a priori de la categoría positiva: 0.5

Calculamos la probabilidades a posteriori de cada categoría para la frase a predecir

test_words <- unlist(strsplit(test, " "))

post_neg <- prior_neg
post_pos <- prior_pos
for(w in test_words){
  if(is.element(w, bow$Word)){
    if(is.element(w, bow_neg$Word)){
      post_neg <- post_neg*bow_neg$likel[bow_neg$Word == w]
    }else{
      post_neg <- post_neg/(n_neg + n_V)
    }
    if(is.element(w, bow_pos$Word)){
      post_pos <- post_pos*bow_pos$likel[bow_pos$Word == w]
    }else{
      post_pos <- post_pos/(n_pos + n_V)
    }
  }
}

cat("\n\nProbabilidad a posteriori de que la frase sea negativa:", post_neg)
## 
## 
## Probabilidad a posteriori de que la frase sea negativa: 2.447867e-12
cat("\nProbabilidad a posteriori de que la frase sea positiva:", post_pos, "\n")
## 
## Probabilidad a posteriori de que la frase sea positiva: 4.692804e-11
norm_const <- post_neg + post_pos
cat("\nProbabilidad a posteriori normalizada de que la frase sea negativa:", post_neg/norm_const)
## 
## Probabilidad a posteriori normalizada de que la frase sea negativa: 0.04957614
cat("\nProbabilidad a posteriori normalizada de que la frase sea positiva:", post_pos/norm_const, "\n")
## 
## Probabilidad a posteriori normalizada de que la frase sea positiva: 0.9504239

TAREA 2

Ejemplo de 4-gramas (NLTK)

1. Importación de librerías y descarga del corpus,

import nltk
from nltk import ngrams
from collections import defaultdict
from numpy.random import choice
from nltk.corpus import cess_esp
nltk.download('cess_esp')
## True
nltk.download('punkt')
## True
nltk.download('punkt_tab')
## True

2. 4-gramas

Creamos todos los posibles 4-gramas del corpus gracias a la función ngrams.

words = nltk.word_tokenize((' '.join(cess_esp.words())))
quadri_grams = list(ngrams(words, 4))
quadri_grams[0]
## ('El', 'grupo', 'estatal', 'Electricité_de_France')

3. Creación del modelo

Creamos un diccionario con la probabilidades que relaciona las tres primeras palabras de un 4-grama con su cuarta palabra.

model = defaultdict(lambda: defaultdict(lambda: 0))
for w1, w2, w3, w4 in quadri_grams:
    model[(w1, w2, w3)][w4] += 1
for w1_w2_w3 in model:
    total_count = float(sum(model[w1_w2_w3].values()))
    for w4 in model[w1_w2_w3]:
        model[w1_w2_w3][w4] /= total_count

Usar 4-gramas produce que el modelo conozca menos opciones que con trigramas.

model['no', 'se', 'han']
## defaultdict(<function <lambda>.<locals>.<lambda> at 0x11ff6d940>, {'hallado': 0.14285714285714285, 'encontrado': 0.14285714285714285, 'mostrado': 0.14285714285714285, 'adherido': 0.14285714285714285, 'adoptado': 0.14285714285714285, 'generado': 0.14285714285714285, 'acabado': 0.14285714285714285})

4. Funciones de predicción

Vamos a crear una función que dadas tres palabras elija la siguiente palabra con mayor probabilidad. Si no hay ninguna palabra devuelve error.

def predict_next_word1(w1, w2, w3):
    next_word_probs = model[w1, w2, w3]
    if next_word_probs:
        return max(next_word_probs, key=next_word_probs.get)
    else:
        return "█"
print(predict_next_word1('no', 'se', 'han'))
## hallado

También vamos a crear otra función que, gracias a la anterior, nos permita ir prediciendo un cierto número de palabras fijándose siempre en las últimas tres palabras predichas.

def prediction1(w1, w2, w3, n):
    result_list = [w1, w2, w3]
    for i in range(n):
        new_word = predict_next_word1(result_list[i], result_list[i + 1], result_list[i + 2])
        result_list.append(new_word)
    return(' '.join(result_list))
print(prediction1('no', 'se', 'han', 17)) # Ejemplo con 17 palabras
## no se han hallado pruebas de la colisión , pero * 0 * no se presentará a la reelección en

Si probamos con un mayor número de palabras, puedemos ver que sigue manteniendo el sentido de la oración.

print(prediction1('no', 'se', 'han', 45)) # Ejemplo con 45 palabras
## no se han hallado pruebas de la colisión , pero * 0 * no se presentará a la reelección en el cargo , renovará su compromiso de reavivar la economía del país , mientras_que la oposición atacará a Mori y a su antiguo lugarteniente , el francés Daniel_Derguy

5. Funciones de predicción aleatorias

Por último, vamos a crear funciones que nos permitan obtener predicciones basadas en la probabilidad de cada 4-grama. Estas funciones elegirán aleatoriamente la siguiente palabra entre los posibles 4-gramas que determinan las tres palabras dadas según su probabilidad. Si no hay ninguna palabra devuelven error.

def predict_next_word2(w1, w2, w3):
    next_word_probs = model[w1, w2, w3]
    if next_word_probs:
        k_nwp = list(next_word_probs)
        p_nwp = list(next_word_probs.values())
        return choice(k_nwp, p = p_nwp)
    else:
        return "█"
  
print(predict_next_word2('no', 'se', 'han'))
## adherido

Cada vez que se ejecuta se obtienen valores diferentes.

def prediction2(w1, w2, w3, n):
    result_list = [w1, w2, w3]
    for i in range(n):
        new_word = predict_next_word2(result_list[i], result_list[i + 1], result_list[i + 2])
        result_list.append(new_word)
    return(' '.join(result_list))
print(prediction2('no', 'se', 'han', 20)) # Ejemplo con 22 palabras
## no se han hallado pruebas de la colisión , pero * 0 * vive de su época `` , dijo * 0 *