<jupyter_start><jupyter_text>Algoritmo de ViterbiPermite hallar la secuencia más probable de estados ocultos que produce una secuencia observada de sucesos, especialmente en el contexto de fuentes de información de Márkov y modelos ocultos de Márkov. Se aplica de forma general en la descodificación de códigos convolucionales usados en redes de telefonía celular digital GSM y CDMA, módems de líneas conmutadas, satélites, comunicaciones espaciales y redes inalámbricas IEEE 802.11. También se usa en reconocimiento del habla, síntesis de habla, diarización, búsqueda de palabras clave, lingüística computacional y bioinformática. https://es.wikipedia.org/wiki/Algoritmo_de_Viterbi https://github.com/rb-one/Curso_Algoritmos_Clasificacion_Texto/blob/main/Notas/notes.md Estos pasos dependen de algoritmo creado en el cuaderno anterior.procesamiento de lenguaje natural Etiquetado.ipynb$t^n = argmax_{t^n}= \prod P(w_i | t_i)P(t_i | t_{i-1})$$v_t(j)  = max_i {v_{t-1}(i) *c_{i,j}*P(parlabra(w)|j)}$<jupyter_code># instalacion de dependencias previas
!pip install conllu
!git clone https://github.com/UniversalDependencies/UD_Spanish-AnCora.git<jupyter_output><empty_output><jupyter_text>Carga del modelo HMM previamente entrenado<jupyter_code># cargamos las probabilidades del modelo HMM del codigo de emision y transmision dadas en 
# procesamiento de lenguaje natural Etiquetado.ipynb
import numpy as np
# se crean bariables para almacenas probabilidades de emision y transision 
transitionProbdict = np.load('transitionHMM.npy', allow_pickle='TRUE').item() # allow_pickle Permitir guardar matrices de objetos utilizando encurtidos Python
emissionProbdict = np.load('emissionHMM.npy', allow_pickle='TRUE').item()
#  muestra las probabilidades condicionales
transitionProbdict
# emissionProbdict
# con esto ya hemos cargado el modelo
# identificamos las categorias gramaticales 'upos' unicas en el corpus
# utilizamos set para evitar que se repitan las categorias gramaticales
stateSet = set([w.split('|')[1] for w in list(emissionProbdict.keys())]) # escogemos w.split('|') para solo selecional la categoria gramatical de cada uno de los elementos
stateSet
# enumeramos las categorias con numeros para asignar a 
# las columnas de la matriz de Viterbi

# esto es un diccionario con relaciones llave valor donde las llaves 
# van ha ser las categorias gramaticales y los valores el numero de la clumna que le va a corresponder en la matriz
tagStateDict = {} 
for i, state in enumerate(stateSet): 
  tagStateDict[state] = i
tagStateDict
# con esto tenemos un diccionario que relaciona cada categoria gramatical
# con un indice que dice cual seria la fila que le corresponde dentro de la matriz de viterbi<jupyter_output><empty_output><jupyter_text>Distribucion inicial de estados latenteslos estados latentes son la primera palabra que hay en cada frase dl corpus<jupyter_code># Calculamos distribución inicial de estados
initTagStateProb = {} # \rho_i^{(0)} creamos este diccionario que son las probabilidades de encontrar cierta categoria gramatical al inicio de la frase
from conllu import parse_incr 
wordList = []
data_file = open("UD_Spanish-AnCora/es_ancora-ud-dev.conllu", "r", encoding="utf-8")
count = 0 # cuenta la longitud del corpus
for tokenlist in parse_incr(data_file):
  count += 1 # incrementamos el contador
  tag = tokenlist[0]['upos'] # cojo el primer elemento con la catagoria gramatical con el upos
  if tag in initTagStateProb.keys():
    initTagStateProb[tag] += 1
  else:
    initTagStateProb[tag] = 1


for key in initTagStateProb.keys(): 
  initTagStateProb[key] /= count # a cada elemento del dicionatio lo divido por el total de frases que hay

initTagStateProb 
# esto nos devuelve las probabilidades por cada una de las categorias
# para calcular la suma de las probabilidades que tiene que ser 1
sum(initTagStateProb.values()) 
# verificamos que la suma de las probabilidades que deben sumar  1 (100%)
np.array([initTagStateProb[k] for k in initTagStateProb.keys()]).sum()<jupyter_output><empty_output><jupyter_text>Construccion Algoritmo de ViterbiDada una secuencia de palabras $\{p_1, p_2, \dots, p_n \}$, y un conjunto de categorias gramaticales dadas por la convención `upos`, se considera la matriz de probabilidades de Viterbi así:$$\begin{array}{c c}\begin{array}{c c c c}\text{ADJ} \\\text{ADV}\\\text{PRON} \\\vdots \\{}\end{array} &\left[\begin{array}{c c c c}\nu_1(\text{ADJ}) & \nu_2(\text{ADJ}) & \dots  & \nu_n(\text{ADJ})\\\nu_1(\text{ADV}) & \nu_2(\text{ADV}) & \dots  & \nu_n(\text{ADV})\\ \nu_1(\text{PRON}) & \nu_2(\text{PRON}) & \dots  & \nu_n(\text{PRON})\\\vdots & \vdots & \dots & \vdots \\ \hdashlinep_1 & p_2 & \dots & p_n \end{array}\right] \end{array}$$Donde las probabilidades de la primera columna (para una categoria $i$) están dadas por: $$\nu_1(i) = \underbrace{\rho_i^{(0)}}_{\text{probabilidad inicial}} \times \underbrace{P(p_1 \vert i)}_{\text{emisión}}$$luego, para la segunda columna (dada una categoria $j$) serán: $$\nu_2(j) = \max_i \{ \nu_1(i) \times \underbrace{P(j \vert i)}_{\text{transición}} \times \underbrace{P(p_2 \vert j)}_{\text{emisión}} \}$$así, en general las probabilidades para la columna $t$ estarán dadas por: $$\nu_{t}(j) = \max_i \{ \overbrace{\nu_{t-1}(i)}^{\text{estado anterior}} \times \underbrace{P(j \vert i)}_{\text{transición}} \times \underbrace{P(p_t \vert j)}_{\text{emisión}} \}$$<jupyter_code>import nltk 
nltk.download('punkt')
from nltk import word_tokenize
# debemos construir la funcion porque el resultado de la funcio sera devolverme la matriz de Vitervi 
# que corresponde a la secuencia de palabras que le vamos a pasar como argumento de entrada a ala funcion
def ViterbiMatrix(secuencia, transitionProbdict=transitionProbdict,
                  emissionProbdict=emissionProbdict, tagStateDict=tagStateDict,
                  initTagStateProb=initTagStateProb):
  seq = word_tokenize(secuencia) # creamos el toquenizador para una secuencia toquenizada
  viterbiProb = np.zeros((17, len(seq)))  # 17 porque upos tiene 17 categorias, matriz(filas,columnas)

  # inicialización primera columna
  for key in tagStateDict.keys():
    tag_row = tagStateDict[key] # generemos una variable de la fila corresondiente a la etiqueta
    word_tag = seq[0].lower()+'|'+key # asignamos de la secuencia e palabras el primer elemento y sumo la key que coresponde a al etiqueta 
    if word_tag in emissionProbdict.keys(): # creamos los elementos de la matriz
      viterbiProb[tag_row, 0] = initTagStateProb[key]*emissionProbdict[word_tag]

  # computo de todas las probabilidades de las siguientes columnas
  for col in range(1, len(seq)): # col para recores las columnas
    for key in tagStateDict.keys(): # hago un for sobre cada llave o categorias gramatica
      tag_row = tagStateDict[key] # asigno la fila relacionada con la categoria gramatical
      word_tag = seq[col].lower()+'|'+key
      if word_tag in emissionProbdict.keys(): # para ver si la etiqueta palabra esta en emission p..
        # miramos estados de la col anterior
        possible_probs = [] # para ello  creamos lista basia que la llenaremos con un for
        for key2 in tagStateDict.keys(): 
          tag_row2 = tagStateDict[key2] # creamos la fila asociada a key2
          tag_prevtag = key+'|'+key2 # consideramos las probabilidades de transiscion y eso se hace ju tando la llave actual con la anterior
          if tag_prevtag in transitionProbdict.keys():
            if viterbiProb[tag_row2, col-1]>0: # aqui y en las 2 siguentes lineas consideramos la ultima parte 
              possible_probs.append(
                  viterbiProb[tag_row2, col-1]*transitionProbdict[tag_prevtag]*emissionProbdict[word_tag])
        viterbiProb[tag_row, col] = max(possible_probs)  # ahora debemos escoger el maximo de todos esos elementos
  
  return viterbiProb

matrix = ViterbiMatrix('El mundo es pequeño')
matrix
# funcion para calcular las etiquetas
def ViterbiTags(secuencia, transitionProbdict=transitionProbdict, emissionProbdict=emissionProbdict, 
            tagStateDict=tagStateDict, initTagStateProb=initTagStateProb):
  seq = word_tokenize(secuencia)
  viterbiProb = np.zeros((17, len(seq)))  # upos tiene 17 categorias

  # inicialización primera columna
  for key in tagStateDict.keys():
    tag_row = tagStateDict[key]
    word_tag = seq[0].lower()+'|'+key
    if word_tag in emissionProbdict.keys():
      viterbiProb[tag_row, 0] = initTagStateProb[key]*emissionProbdict[word_tag]

  # computo de las siguientes columnas
  for col in range(1, len(seq)):
    for key in tagStateDict.keys():
      tag_row = tagStateDict[key]
      word_tag = seq[col].lower()+'|'+key
      if word_tag in emissionProbdict.keys():
        # miramos estados de la col anterior
        possible_probs = []
        for key2 in tagStateDict.keys(): 
          tag_row2 = tagStateDict[key2]
          tag_prevtag = key+'|'+key2
          if tag_prevtag in transitionProbdict.keys():
            if viterbiProb[tag_row2, col-1]>0:
              possible_probs.append(
                  viterbiProb[tag_row2, col-1]*transitionProbdict[tag_prevtag]*emissionProbdict[word_tag])
        viterbiProb[tag_row, col] = max(possible_probs)
# hasta aqui repetimos el paso anterior

# ahora agregaremos un paso extra donde realizaremos
    # contruccion de secuencia de tags o etiquetas
    res = [] # contruimos lista basia llamada res
    for i, p in enumerate(seq): # creamos for para recorrer cada palabra de la secuencia y ademas la vamos enumerando las palabras
      for tag in tagStateDict.keys(): #para recorrer todos los tags para que pueda ver la probabilidad de que esa palabra este asociada a lasetiquetas
        if tagStateDict[tag] == np.argmax(viterbiProb[:, i]):
          res.append((p, tag)) # este paso es para  agregar lo anterior a la lista
                                # aqui tendreos una lista de palabra etiqueta
  return res

ViterbiTags('el mundo es muy pequeño')
ViterbiTags('estos instrumentos han de rasgar')<jupyter_output><empty_output><jupyter_text>Entrenamiento directo de HMM con NLTKclase en python (NLTK) de HMM: https://www.nltk.org/_modules/nltk/tag/hmm.html  Ejemplo con el Corpus Treebank en ingles<jupyter_code>#@title ejemplo con el Corpus Treebank en ingles
import nltk
nltk.download('treebank')
from nltk.corpus import treebank
train_data = treebank.tagged_sents()[:3900] # esto es asi para coger una parte de los datos para entrenamiento y la otra parte para prueba
# y escogemos hasta la sentencia 3900 y dejamos el resto para test<jupyter_output><empty_output><jupyter_text>estructura de la data de entrenamiento<jupyter_code>#@title estructura de la data de entrenamiento
train_data<jupyter_output><empty_output><jupyter_text>HMM pre-construido en NLTK<jupyter_code>#@title HMM pre-construido en NLTK
from nltk.tag import hmm # cargamos clase para poder utilizar modelo
tagger = hmm.HiddenMarkovModelTrainer().train_supervised(train_data) # parapedir que sea un entrenamiento supervisado
# tagger = hmm.HiddenMarkovModelTagger.train(train_data) 
tagger
tagger.tag("<NAME> will get old".split()) # aquii estamos tokenizando con un .split()
#@title training accuracy
# para ver el porcentaje de evaluacion del modelos, osea su presicion sobre el conjunto de entrenamiento
tagger.evaluate(treebank.tagged_sents()[:3900])<jupyter_output><empty_output><jupyter_text>Ejercicio de prácticaObjetivo: Entrena un HMM usando la clase hmm.HiddenMarkovModelTrainer() sobre el dataset UD_Spanish_AnCora. 1. **Pre-procesamiento:** En el ejemplo anterior usamos el dataset en ingles `treebank`, el cual viene con una estructura diferente a la de `AnCora`, en esta parte escribe código para transformar la estructura de `AnCora` de manera que quede igual al `treebank` que usamos así:$$\left[ \left[ (\text{'El'}, \text{'DET'}), (\dots), \dots\right], \left[\dots \right] \right]$$<jupyter_code># desarrolla tu código aquí 
# Instalamos conllu para leer el corpus
!pip install conllu
# Bajamos el corpus de AnCora
!git clone https://github.com/UniversalDependencies/UD_Spanish-AnCora.git
from conllu import parse_incr
data_file = open("UD_Spanish-AnCora/es_ancora-ud-dev.conllu", "r", encoding="utf-8") 


# Hacemos la transformacion del corpus al formato requerido
wordList = [] # creamos lista basia
for tokenlist in parse_incr(data_file): 
  wordList2 = []
  for token in tokenlist:
    tag = token['upos']
    valor = token['form']
    wordList2.append((valor,tag)) 
  wordList.append(wordList2)<jupyter_output><empty_output><jupyter_text>2. **Entrenamiento:** Una vez que el dataset esta con la estructura correcta, utiliza la clase `hmm.HiddenMarkovModelTrainer()` para entrenar con el $80 \%$ del dataset como conjunto de `entrenamiento` y $20 \%$ para el conjunto de `test`.**Ayuda:** Para la separacion entre conjuntos de entrenamiento y test, puedes usar la funcion de Scikit Learn: https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.htmlEn este punto el curso de Machine Learning con Scikit Learn es un buen complemento para entender mejor las funcionalidades de Scikit Learn: https://platzi.com/cursos/scikitlearn-ml/<jupyter_code># desarrolla tu código aquí

import nltk
from nltk.tag import hmm
from sklearn.model_selection import train_test_split<jupyter_output><empty_output><jupyter_text>3. **Validación del modelo:** Un vez entrenado el `tagger`, calcula el rendimiento del modelo (usando `tagger.evaluate()`) para los conjuntos de `entrenamiento` y `test`.<jupyter_code>#desarrolla tu código aquí
# Separamos el corpus
wordList_train, wordList_test= train_test_split(wordList, test_size=0.20, random_state=42)

# Entrenamos el modelo
tagger = hmm.HiddenMarkovModelTrainer().train_supervised(wordList_train)
tagger

print(tagger.evaluate(wordList_test))
print(tagger.evaluate(wordList_train))<jupyter_output><empty_output><jupyter_text>Procesamiento de lenguaje naturalkERNEL: es una funcion matematica que toma mediciones que se comportan de manera no lineal Tenicas de clasificacion:basadas en la teoria de la probabilidadbasadas en la teoria de la informacionbasadas en los espacios vectorialesClasificacion de Palabras:como identificacion de generon,etiquetado POSBloqueo de palabras ofensivasClasificacion de documento:analisis de sentimientos,topicos de coversacion,Priorizacion de CRMs(bases de petisiones de usarion por comunicaquin de CRM)  Tareas de clasificación con NLTK--- https://www.aprendemachinelearning.com/arbol-de-decision-en-python-clasificacion-y-prediccion/  Modelos de clasificación en Python: nombres<jupyter_code>import nltk, random
nltk.download('names') # descaga data set importante
from nltk.corpus import names # pertimte extraer esa dataset<jupyter_output><empty_output><jupyter_text>Función básica de extracción de atributos<jupyter_code># definición de atributos relevantes
def atributos(palabra):
	return {'ultima_letra': palabra[-1]}
# escribiremos la lista de tuplas, esto contine nombres masculinos y femeninos separados en distintos archivos
tagset = ([(name, 'male') for name in names.words('male.txt')] +
          [(name, 'female') for name in names.words('female.txt')])
tagset[:10]
# para mexclar los elementos y evitar el sesgo
random.shuffle(tagset)
tagset[:10] # mustrea los primeros 10 elementos
# creo una lista leyendo los elementos o atributos de la lista anterior
# n= nombre, g = genero
fset = [(atributos(n), g) for (n, g) in tagset]
train, test = fset[500:], fset[:500] # divido en entrenamiento y prueba
# digamos cogiendo los 500 en adelante para train y el resto para test<jupyter_output><empty_output><jupyter_text>Modelo de clasificación Naive Bayes<jupyter_code># entrenamiento del modelo NaiveBayes
classifier = nltk.NaiveBayesClassifier.train(train)<jupyter_output><empty_output><jupyter_text>Verificación de algunas predicciones<jupyter_code>classifier.classify(atributos('camila'))
classifier.classify(atributos('peter'))<jupyter_output><empty_output><jupyter_text>Performance del modelo<jupyter_code># para calcular la metrica accuracy del modelo classifier y sobre los datos del test
# esto nos dice el porcentaje de prediccion del modelo
print(nltk.classify.accuracy(classifier, test))
# precicion del modelo sobre los datos de entrenamiento
print(nltk.classify.accuracy(classifier, train))<jupyter_output><empty_output><jupyter_text>Mejores atributos<jupyter_code># para evitar escribir el alfabeto utilizamos:
# import string
# string.ascii_lowercase

import string 
def mas_atributos(nombre):
    atrib = {} #$ creamos diccionario basio para llenarlo con mas atributos
    atrib["primera_letra"] = nombre[0].lower() # primer caracter. y lower para convertir en minuscula
    atrib["ultima_letra"] = nombre[-1].lower() # escogemos el ultimo caracter
    for letra in string.ascii_lowercase:
        atrib["count({})".format(letra)] = nombre.lower().count(letra) # cuenta las veces que aparece la letra
        atrib["has({})".format(letra)] = (letra in nombre.lower())  # indica si aparece como true y de lo contrario false
    return atrib
mas_atributos('jhon')
fset = [(mas_atributos(n), g) for (n, g) in tagset]
train, test = fset[500:], fset[:500]
classifier2 = nltk.NaiveBayesClassifier.train(train)
print(nltk.classify.accuracy(classifier2, test))
# mejoro el modelo<jupyter_output><empty_output><jupyter_text>Ejercicio de prácticaObjetivo: Construye un classificador de nombres en español usando el siguiente dataset: https://github.com/jvalhondo/spanish-names-surnames Preparación de los datos: con un git clone puedes traer el dataset indicado a tu directorio en Colab, luego asegurate de darle el formato adecuado a los datos y sus features para que tenga la misma estructura del ejemplo anterior con el dataset names de nombres en ingles.Piensa y analiza: ¿los features en ingles aplican de la misma manera para los nombres en español?<jupyter_code># Cargamos los datasets
!git clone https://github.com/jvalhondo/spanish-names-surnames
import numpy as np
tag_men = np.genfromtxt('/content/spanish-names-surnames/male_names.csv', skip_header=1, delimiter=',', dtype=('U20','i8','f8'))
tag_women = np.genfromtxt('/content/spanish-names-surnames/female_names.csv', skip_header=1, delimiter=',', dtype=('U20','i8','f8'))

# escribiremos la lista de tuplas, esto contine nombres masculinos y femeninops separados en distintos archivos
f_set = ([(name[0],'male') for name in tag_men] +
         [(name[0],'female') for name in tag_women]) # iniciamos selecionando la primera letra de cada nombre con name[0]

import random # mexclamos las listas M y F
random.shuffle(f_set)


# Funcion con mejores atributos 
# entrenamos un modelo sencillo usando el mismo feature de la última letra del nombre
def atributos2(nombre):
    atrib = {} # creamos dicionario basio para llenarlo con mas atributos
    atrib["ultima_letra"] = nombre[-1].lower() #Ultima letra
    atrib["ultimas_cuatro_letras"] = nombre[-1:-5:-1].lower() # ultimas 4 letras
    return atrib

f_varios_atributo = [(atributos2(n), g) for (n, g) in f_set]

#Usamos el 75% de los datos para train y 25% para test
f_varios_atributo_train, f_varios_atributo_test = train_test_split(f_varios_atributo, test_size=0.25, random_state=45)

#Entrenamos el modelo  y Probamos
classifier_2 = nltk.NaiveBayesClassifier.train(f_varios_atributo_train)
print(classifier_2.classify(atributos('Juan')))
print(nltk.classify.accuracy(classifier_2, f_varios_atributo_test))<jupyter_output><empty_output><jupyter_text>**Clasificación de palabras (por género de nombre)**<jupyter_code>import nltk, random
# para evitarse escribir el alfabeto mejor hacer:
import string 
# string.ascii_lowercase

nltk.download('names') # descaga data set importante
from nltk.corpus import names  # pertimte extraer esa dataset<jupyter_output><empty_output><jupyter_text>**Función básica de extracción de atributos**<jupyter_code># definición que extraera los atributos relevantes
def atributos(palabra):
	return {'ultima_letra': palabra[-1]} #extrae la ultima letra

'''construimos la lista de tuplas esto se hacer recoriendo
 las palabras o nombre juntando las listas de los nombres mascuinos y femeninos'''
tagset = ([(name, 'male') for name in names.words('male.txt')] + [(name, 'female') for name in names.words('female.txt')])
tagset[:10]
random.shuffle(tagset) # mexclamos la lista para evitar sesgos
tagset[:10]
# creamos los atributos de los nombres
# creo una lista lellendo los atributos de la lista anterior
fset = [(atributos(n), g) for (n, g) in tagset]
train, test = fset[500:], fset[:500] # separamos los datops en train y test<jupyter_output><empty_output><jupyter_text>**Modelo de clasificación Naive Bayes**<jupyter_code># entrenamiento del modelo NaiveBayes
classifier = nltk.NaiveBayesClassifier.train(train)<jupyter_output><empty_output><jupyter_text>**Verificación de algunas predicciones**<jupyter_code># clasifica mos los atributos de los nombres
classifier.classify(atributos('amanda'))
classifier.classify(atributos('peter'))<jupyter_output><empty_output><jupyter_text>**Performance del modelo**<jupyter_code>#verificamos el performant  sobre todo el conjunto de test
# esto se hace calculando o clasificando  la metrica accuracy 
print(nltk.classify.accuracy(classifier, test))
# esto nos indica que porcentaje clasifica correctamente
# esto clasifica la metrica de train
print(nltk.classify.accuracy(classifier, train))<jupyter_output><empty_output><jupyter_text>**Mejores atributos**<jupyter_code># definiremos mejores atributos donde el argumento de entreada sera nombre



def mas_atributos(nombre):
    atrib = {} # creamos diccionario basio el cual lo llenaremos con mas atributos
    atrib["primera_letra"] = nombre[0].lower() # primer caracter. y lower para convertir en minuscula
    atrib["ultima_letra"] = nombre[-1].lower() # escogemos el ultimo caracter
    for letra in string.ascii_lowercase:
        atrib["count({})".format(letra)] = nombre.lower().count(letra) # cuenta el numero de bese que aparece la letra
        # format me permite pasar letra dentro del srting  que seria como la llave de ese diccionario
        atrib["has({})".format(letra)] = (letra in nombre.lower()) # pregunta si tiene o no tiene la letra
    return atrib # para que me retorna la lista de atributos
mas_atributos('jhon')
# armamos nuestra lista de atributos
fset = [(mas_atributos(n), g) for (n, g) in tagset]
train, test = fset[500:], fset[:500]
classifier2 = nltk.NaiveBayesClassifier.train(train)
print(nltk.classify.accuracy(classifier2, test))<jupyter_output><empty_output><jupyter_text>ejercicio Objetivo: Construye un classificador de nombres en español usando el siguiente dataset: https://github.com/jvalhondo/spanish-names-surnamesPreparación de los datos: con un git clone puedes traer el dataset indicado a tu directorio en Colab, luego asegurate de darle el formato adecuado a los datos y sus features para que tenga la misma estructura del ejemplo anterior con el dataset names de nombres en ingles.Piensa y analiza: ¿los features en ingles aplican de la misma manera para los nombres en español?Entrenamiento y performance del modelo: usando el classificador de Naive Bayes de NLTK entrena un modelo sencillo usando el mismo feature de la última letra del nombre, prueba algunas predicciones y calcula el performance del modelo.Mejores atributos: Define una función como atributos2() donde puedas extraer mejores atributos con los cuales entrenar una mejor version del clasificador. Haz un segundo entrenamiento y verifica como mejora el performance de tu modelo. ¿Se te ocurren mejores maneras de definir atributos para esta tarea particular?<jupyter_code># Cargamos los datasets
!git clone https://github.com/jvalhondo/spanish-names-surnames
import numpy as np
tag_men = np.genfromtxt('/content/spanish-names-surnames/male_names.csv', skip_header=1, delimiter=',', dtype=('U20','i8','f8'))
tag_women = np.genfromtxt('/content/spanish-names-surnames/female_names.csv', skip_header=1, delimiter=',', dtype=('U20','i8','f8'))

f_set = [(name[0],'male') for name in tag_men] + [(name[0],'female') for name in tag_women]
import random
random.shuffle(f_set)

# Funcion con mejores atributos 
def atributos2(nombre):
    atrib = {}
    atrib["ultima_letra"] = nombre[-1].lower() #Ultima letra
    atrib["ultimas_dos_letra"] = nombre[-1:-5:-1].lower() #ultimas 4 letras
    return atrib

f_varios_atributo = [(atributos2(n), g) for (n, g) in f_set]

#Usamos el 80% de los datos para train y 20% para test
f_varios_atributo_train, f_varios_atributo_test = train_test_split(f_varios_atributo, test_size=0.20, random_state=45)

#Entrenamos y Probamos
classifier_2 = nltk.NaiveBayesClassifier.train(f_varios_atributo_train)
print(classifier_2.classify(atributos('Juan')))
print(nltk.classify.accuracy(classifier_2, f_varios_atributo_test))<jupyter_output><empty_output><jupyter_text>**Clasificación de documentos (email spam o no spam)**https://github.com/pachocamacho1990/datasets: contiene una carpeta email, que contiene datos y unos corpus<jupyter_code>!git clone https://github.com/pachocamacho1990/datasets
import pandas as pd
import numpy as np
nltk.download('punkt') # punkt(puntualizacion) es un tokenizador estandar
nltk.download('averaged_perceptron_tagger') # average_perceptron  es un  tagger o etiquetador por defecto en ingles 
from nltk import word_tokenize # TOKENIZADOR DE PALABRAS basado en el tokenizador punkt
# importamos dataframe, como este archivo no tiene nombres de columnas por lo que se le daran nombres
# name donde la primera columnas es clase que indicara categorias si es o no es spam donde -1 es spam y contenido esla otra columna
df = pd.read_csv('datasets/email/csv/spam-apache.csv', names = ['clase','contenido'])
# para clasificar primero debemos separar en tokens y luego definir atributos apartir de los tokens
df['tokens'] = df['contenido'].apply(lambda x: word_tokenize(x)) # aqui creamos nueva columna
# esta funcion lo que esta haciendo es aplicar un word_tokenize a cada una delas filas de la columna contenido
df.head()
df['tokens'].values[0] # pedimos los valores de la coluna tokens y de esos valores dame el primero
# con FreqDist que es una distribucion de frecuencias que lo que hace es que para cada
# palabra que le pase como argumento de esta funcion determian cuantasveces aparece esta palabra
all_words = nltk.FreqDist([w for tokenlist in df['tokens'].values for w in tokenlist]) # cresmos lista con un doble for dentro
# all_words #  muestra cada palabra con el numero de beses que aparecio
top_words = all_words.most_common(200) # para buscar es las mas frecuentes palabras con most_common

# definimos funcionpara extraer atributos de un documento
def document_features(document):
    document_words = set(document) # cogemos el documento y le pasamos la funcion set que lo que hace es que si hay palaras repetidas escoge solo las palabras unicas
    features = {} # definimos un diccionario de atributos
    for word in top_words: # para recores las  palabras m,as popilares
        features['contains({})'.format(word[0])] = (word[0] in document_words) # por medio de format le paso una variable externa al string que aqui sera  word
    return features
document_features(df['tokens'].values[0]) # probamos selecionando tokens y escojamos los valores y de estos la primera fila   
# construiresmos modelo de clasificacion
 #para el for texto y clase primero cogera los textos de la columna tokens del dataframe y las clases del dataframe
fset = [(document_features(texto), clase) for texto, clase in zip(df['tokens'].values, df['clase'].values)]
random.shuffle(fset) # mexclamos para evitar sesgos
train, test = fset[:200], fset[200:] # dividimos en train y test
# aplicamos modelo con nltk para entrenar modelo
classifier = nltk.NaiveBayesClassifier.train(train)
#verificamos el performant  sobre todo el conjunto de test
# esto se hace calculando o clasificando  la metrica accuracy 
print(nltk.classify.accuracy(classifier, test))
# esto nos indica que % se clasifica correctamente
classifier.show_most_informative_features(5)
# filtramos el dataframe  de la columna clase donde sea spam y miramos el contenido
df[df['clase']==-1]['contenido']<jupyter_output><empty_output><jupyter_text>reto ¿Como podrías construir un mejor clasificador de documentos?Dataset más grande: El conjunto de datos que usamos fue muy pequeño, considera usar los archivos corpus que estan ubicados en la ruta: datasets/email/plaintext/Limpieza: como te diste cuenta no hicimos ningun tipo de limpieza de texto en los correos electrónicos. Considera usar expresiones regulares, filtros por categorias gramaticales, etc ...<jupyter_code># !git clone https://github.com/pachocamacho1990/datasets
import os
import nltk
import random
from nltk import word_tokenize
from nltk.collocations import *
import pandas as pd
nltk.download("punkt")

# Funciones para cargar los datasets

# Get Text and labels from folders with plain text files
from os import listdir
from zipfile import ZipFile
from nltk.tokenize import RegexpTokenizer
nltk.download('stopwords')
from nltk.corpus import stopwords
stopword = stopwords.words('english')

corp_path = '/content/datasets/email/plaintext/'
files_path = ['{}'.format(corp_path) + f for f in listdir (corp_path)]

df = pd.DataFrame(columns=["clase", "token"])

tokenizer = RegexpTokenizer("[\w+.]+")

for folder in files_path:
  zf = ZipFile(folder)
  files = [f for f in ZipFile.namelist(ZipFile(folder)) if f.endswith('.txt')]
  for i, file_name in enumerate(files):
    spam_ham = -1 if file_name.endswith('spam.txt') else 1
    read = zf.open(file_name).read().decode("ISO-8859-1").lower()
    tokens = tokenizer.tokenize(read)
    token_free = [word for word in tokens if word not in stopword]
    datos = {'clase': spam_ham, 'token': token_free}
    df = df.append(datos, ignore_index=True)

print(df)
# Descomprimir ZIP
import zipfile
fantasy_zip = zipfile.ZipFile('/content/datasets/email/plaintext/corpus1.zip')
fantasy_zip.extractall('/content/datasets/email/plaintext')
fantasy_zip.close()

# Creamos un listado de los archivos dentro del Corpus1 ham/spam
from os import listdir

path_ham = "/content/datasets/email/plaintext/corpus1/ham/"
filepaths_ham = [path_ham+f for f in listdir(path_ham) if f.endswith('.txt')]

path_spam = "/content/datasets/email/plaintext/corpus1/spam/"
filepaths_spam = [path_spam+f for f in listdir(path_spam) if f.endswith('.txt')]

# Creamos la funcion para tokenizar y leer los archivos 

def abrir(texto):
  with open(texto, 'r', errors='ignore') as f2:
    data = f2.read()
    data = word_tokenize(data)
  return data

# Creamos la lista tokenizada del ham
list_ham = list(map(abrir, filepaths_ham))
# Creamos la lista tokenizada del spam
list_spam = list(map(abrir, filepaths_spam))

nltk.download('stopwords')

# Separamos las palabras mas comunes
all_words = nltk.FreqDist([w for tokenlist in list_ham+list_spam for w in tokenlist])
top_words = all_words.most_common(250)

# Agregamos Bigramas
bigram_text = nltk.Text([w for token in list_ham+list_spam for w in token])
bigrams = list(nltk.bigrams(bigram_text))
top_bigrams = (nltk.FreqDist(bigrams)).most_common(250)


def document_features(document):
    document_words = set(document)
    bigram = set(list(nltk.bigrams(nltk.Text([token for token in document]))))
    features = {}
    for word, j in top_words:
        features['contains({})'.format(word)] = (word in document_words)

    for bigrams, i in top_bigrams:
        features['contains_bigram({})'.format(bigrams)] = (bigrams in bigram)
  
    return features

# Juntamos las listas indicando si tienen palabras de las mas comunes
import random
fset_ham = [(document_features(texto), 0) for texto in list_ham]
fset_spam = [(document_features(texto), 1) for texto in list_spam]
fset = fset_spam + fset_ham[:1500]
random.shuffle(fset)

# Separamos en las listas en train y test
from sklearn.model_selection import train_test_split
fset_train, fset_test = train_test_split(fset, test_size=0.20, random_state=45)

# Entrenamos el programa
classifier = nltk.NaiveBayesClassifier.train(fset_train)

# Probamos y calificamos
classifier.classify(document_features(list_ham[34]))
print(nltk.classify.accuracy(classifier, fset_test))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09282216574722618, 'fraction_numerical': 0.008429926238145416, 'mean_word_length': 5.370582428430405, 'pattern_counts': {'":': 0, '<': 106, '<?xml version=': 0, '>': 108, 'https://': 15, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '26', 'dir': 'jupyter-structured-clean-dedup', 'id': '376470', 'license': 'MIT', 'n_tokens_mistral': 11220, 'n_tokens_neox': 10905, 'n_words': 3319, 'path': 'procesamiento_de_lenguaje_natural_algorit_Viterbi.ipynb', 'repo_name': 'WuilsonEstacio/Procesamiento-de-lenguaje-natural', 'size': '32273'}