#!/usr/bin/env python # coding: utf-8 # # Contando N-Gramas # In[1]: from nltk.corpus import gutenberg sents = list(gutenberg.sents('austen-emma.txt')) # Primero vemos cómo imprimir todos los trigramas de una sola oración: # In[2]: sent = sents[0] n = 3 # trigramas for i in range(len(sent) - n + 1): print(sent[i:i+n]) # Ahora veamos cómo contar los trigramas de todas las oraciones: # In[13]: from collections import defaultdict count = defaultdict(int) for sent in sents: for i in range(len(sent) - n + 1): ngram = tuple(sent[i:i+n]) # los diccionarios no pueden guardar listas, pero sí tuplas count[ngram] += 1 # In[91]: # count # El código sirve para n-gramas en general. # # Tareas pendientes: # - Agregar marcadores de principio y final de oración # - Contar n-gramas y (n-1)-gramas al mismo tiempo. # # Generando Lenguaje Natural # # El siguiente modelo de bigramas se aprende a partir de dos oraciones: # - "el gato come pescado" # - "la gata come salmón" # # In[78]: probs = { '': {'el': 0.5, 'la': 0.5}, # '': {'el': 0.6, 'la': 0.2, 'los': 0.1, 'las': 0.1}, 'el': {'gato': 1.0}, 'gato': {'come': 1.0}, 'come': {'pescado': 0.5, 'salmón': 0.5}, 'pescado': {'.': 1.0}, '.': {'': 1.0}, 'la': {'gata': 1.0}, 'gata': {'come': 1.0}, 'salmón': {'.': 1.0}, } list(probs[''].items()) # convertir un diccionario a lista de pares # Cada entrada del diccionario contiene una distribución discreta finita para la palabra siguiente dada la palabra anterior. Samplear de una distribución discreta finita es tan fácil como samplear un número al azar entre 0 y 1 y ver en qué región cae (ver [Wikipedia](https://en.wikipedia.org/wiki/Pseudo-random_number_sampling#Finite_discrete_distributions)). # # Empezamos sampleando la primer palabra: # In[88]: from random import random def sample(problist): r = random() # entre 0 y 1 i = 0 word, prob = problist[0] acum = prob while r > acum: i += 1 word, prob = problist[i] acum += prob return word sample(list(probs[''].items())) # Podemos ver que el resultado del sampleo se corresponde con las probabilidades: # In[90]: results = [sample(list(probs[''].items())) for i in range(1000)] from collections import Counter print(Counter(results)) # **Observaciones:** # - Si se ordena la lista de probabilidades de mayor a menor, el sampling es más rápido. # - El sampling también se puede hacer usando [random.choices](https://docs.python.org/3/library/random.html#random.choices) de python # ó [random.choice](https://stackoverflow.com/questions/11373192/generating-discrete-random-variables-with-specified-weights-using-scipy-or-numpy) de numpy. # # Ahora veamos cómo samplear una oración completa: # In[112]: word = '' while word != '': problist = list(probs[word].items()) word = sample(problist) print(word) # Acá se ve que se pueden generar oraciones nuevas (no vistas en tiempo de entrenamiento). # # Tareas pendientes: # - adaptar el código a n-gramas en general: usar tuplas como claves en probs! # - precalcular las listas ordenadas de mayor a menor (ver sorted_prob en los tests)