A煤n no tienes acceso a esta clase

Crea una cuenta y contin煤a viendo este curso

Modelos de clasificaci贸n en Python: nombres

19/26
Recursos

Aportes 14

Preguntas 1

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesi贸n.

Creo que en este punto es que debemos entender una parte clave de hacer data science o ai, y es que no se trata de solo mejorar un accuracy y subirlo, sino que es muy importante tener un 鈥渆ntendimiento del contexto de problema鈥. Veo que aunque se pueden hacer pruebas tomando m谩s o menos letras, falta realmente el conocimiento del campo y el punto de vista que un experto ling眉ista podr铆a dar sobre el tema, por que f谩cilmente a煤n cuando suban el accuracy a 99% no significa que si traemos un nuevo set de datos el comportamiento va a ser tan bueno, claramente este tema de nombre es complejo(es com煤n encontrar nombres que rompen cualquier regla supuesta, mezclas de idiomas, 鈥tc) y f谩cilmente podemos caer en temas de overfit, adem谩s recuerden tambi茅n que hay otras m茅tricas m谩s all谩 de solo hablar de accuracy, por ejemplo hablar de un F1-score donde integremos un factor de sensbilidad(recall) de nuestro modelo tambi茅n es 煤til.

Completado el reto!

Pueden ver mi soluci贸n: Aqui

馃槂

Leyendo la 煤ltima y las ultimas 4 letras se llega a un 90% de Accuracy

# 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))```

para evitarse escribir el alfabeto mejor hacer:

import string 

string.ascii_lowercase

Concurro con mis compa帽eros que dicen que utilizar las ultimas cuatro letras del nombre aumenta el 鈥performance鈥 del modelo, aqu铆 estan algunas comparaciones de m茅tricas utrilizando diferentes atributos:
.

.
Sin embargo ninguna configuraci贸n de atributos puede predecir con exactitud que el nombre Jose Maria es de hombre por alguna raz贸n 馃槄, cosa rara porque nosotros sabemos que :
jose Maria -> Hombre
Maria Jose -> Mujer
(Hola si tienes uno de esos nombres 馃槂!)
.

.
Bueno de todos modos, reto cumplido.
c贸digo -> https://colab.research.google.com/drive/1InK3xAPezE_dWaOgnEYpxH0PRrfDrseI?usp=sharing

# Clonamos el repositorio
!git clone https://github.com/jvalhondo/spanish-names-surnames

Leo los dos archivos con pandas, los paso a un dataframe

import pandas as pd
df_female = pd.read_csv("spanish-names-surnames/female_names.csv", delimiter=",", encoding="utf-8")
df_male = pd.read_csv("spanish-names-surnames/male_names.csv", delimiter=",", encoding="utf-8")

Construyo el dataset

female_names=list(df_female['name'])
male_names=list(df_male['name'])

tagset = [(n, 'female') for n in female_names] + [(n, 'male') for n in male_names]
random.shuffle(tagset)
tagset[:10]

Versi贸n 1 (Definiendo atributos)

def atributos_sp(nombre):
  attr = {}
  attr["ultima_letra"] = nombre[-1].lower()
  attr["cant_nombres"] = str(len(nombre.split(" ")))
  attr["tiene_dos_nombres"] = True if (len(nombre.split(" "))==2) else False
  attr["ultima_letra_seg_nombre"] = nombre.split(" ")[1][-1].lower() if (len(nombre.split(" "))==2) else False
  return attr

Construyo datos de entrenamiento y pruebas

fset = [(atributos_sp(n), g) for (n, g) in tagset if type(n)==str]
train, test = fset[500:], fset[:500]

Entrenamos

classifier3 = nltk.NaiveBayesClassifier.train(train)

Validamos el performance

print(nltk.classify.accuracy(classifier3, test))
// Se obtiene un 0,798 

Aqu铆 mi codigo


#%%
import nltk
import random
import pandas as pd

#%%
#### importar datos de nombres de hombres y mujeres con pandas
df_male = pd.read_csv('./spanish-names-surnames/male_names.csv')
df_female= pd.read_csv('./spanish-names-surnames/female_names.csv')

#%%
#### agregar fila male en df_male
df_male = df_male.assign(gender='male')
df_female = df_female.assign(gender='female')
#%%
#### unir dos dataframes
df = pd.merge(df_male, df_female, 'outer')
#%%
#### Definir lista de tuplas
tagset = [(fila['name'], fila['gender']) for i,fila in df.iterrows()]
random.shuffle(tagset)

# %%
#### Definir atributos
def atributes(word):
    atrib = {}
    atrib['last_letter'] = word[-1].lower()
    atrib['first_letter'] = word[0].lower()
    for letra in 'abcdefghijklmnopqrstuvwxyz':
        atrib['count({})'.format(letra)] = word.lower().count(letra)
        atrib['has({})'.format(letra)] = (letra in word.lower())
    return atrib


# %%
### Crear una segunda lista de atributos
fset = [(atributes(str(n)), g) for (n,g) in tagset]

# %%
train, test = fset[(int((len(fset)*10)/100)):], fset[:(int((len(fset)*10)/100))]
# %%
classfier = nltk.NaiveBayesClassifier.train(train)
# %%
### Medir performance del modelo
nltk.classify.accuracy(classfier, test)
# %%
classfier.classify(atributes('ariel'))

Hola, aqu铆 dejo algunas funciones que hice para apoyarme en la clase, una es para obtener directamente el train y test dando el tagset y el tama帽o que queramos. La otra funci贸n regresa el modelo entrenado y lo eval煤a a partir de los mismos par谩metros que la funci贸n anterior. Este es el c贸digo

def mas_atributos(nombre):
  atrib = {}
  atrib['primera_letra'] = nombre[0].lower()
  atrib['ultima_letra'] = nombre[-1].lower()
  # iteramos el abecedario
  for letra in 'abcdefghijklmn帽opqrstuvwxyz':
    # contamos cuantas letras y de cuales tiene el nombre
    atrib[f'count({letra})'] = nombre.lower().count(letra)
    # vemos si la letra esta en la palabra
    atrib[f'has({letra})'] = (letra in nombre.lower())
  return atrib

# una funci贸n para obtener train y test directamente
def get_fsets(nombres, n):
  fset = [(mas_atributos(nombre), genero) for (nombre ,genero) in tagset]
  train, test = fset[n:], fset[:n]
  return train, test

# evaluar el modelo directamente 
def try_model(nombres, n):
  # obtenemos train y test
  train, test = get_fsets(nombres, 500)
  # intanciamos y evaluamos el modelo
  model = nltk.NaiveBayesClassifier.train(train)
  # lo evaluamos y muestra el resultado
  print(f'accuracy: {nltk.classify.accuracy(model, test)}')
  # retorna el modelo y los datos
  return model

train, test = get_fsets(tagset,500)
model = try_model(tagset, 500)

Aqui mi aportacion

# escribe tu c贸digo aqu铆
!git clone https://github.com/jvalhondo/spanish-names-surnames

import pandas as pd
from sklearn.model_selection import train_test_split

male_names = pd.read_csv('/content/spanish-names-surnames/male_names.csv')
female_names =  pd.read_csv('/content/spanish-names-surnames/female_names.csv')
female_names.dropna(axis=0, inplace=True)

tagnames = [(name.lower(), 'male') for name in male_names['name']] + [(name.lower(), 'female') for name in female_names['name']] 
random.shuffle(tagset)

# definici贸n de atributos relevantes
def atributos(palabra):
	return {'ultima_letra': palabra[-1]}

# Creando dataset con atributos
fset = [(atributos(n), g) for (n, g) in tagset]

# Dividiendo en train_data y test_data
train_data, test_data = train_test_split(fset, test_size=0.20)

# Evaluacion
classifier = nltk.NaiveBayesClassifier.train(train_data)

print(f'test_data accuracy: {nltk.classify.accuracy(classifier, test_data)}')
print(f'train_data accuracy {nltk.classify.accuracy(classifier, train_data)}')

test_data accuracy: 0.7482693517935809
train_data accuracy 0.7664830841856806

Resultados

test_data accuracy: 0.7482693517935809
train_data accuracy 0.7664830841856806

Mejorando atributos

def atributos2(nombre):
    atrib = {}
    atrib["nombre"] = nombre.lower()
    atrib["primera_letra"] = nombre[0].lower()
    atrib["ultima_letra"] = nombre[-1].lower()
    atrib["primeras_dos_letras"] = nombre[:2].lower()
    atrib["ultimas_dos_letras"] = nombre[-2:].lower()

    for letra in 'abcdefghijklmnopqrstuvwxyz':
        #atrib 3. numero de veces aparece la letra
        atrib["count({})".format(letra)] = nombre.lower().count(letra)
        #atrib 4. si tiene o no la letra
        atrib["has({})".format(letra)] = (letra in nombre.lower())
    return atrib

fset2 = [(mas_atributos(n), g) for (n, g) in tagset]

# Dividiendo en train_data y test_data
train_data2, test_data2 = train_test_split(fset2, test_size=0.20)

# Evaluacion
classifier2 = nltk.NaiveBayesClassifier.train(train_data2)
print(f'train_data accuracy: {nltk.classify.accuracy(classifier2, train_data2)}')
print(f'test_data accuracy {nltk.classify.accuracy(classifier2, test_data2)}')

Resultados

train_data accuracy: 0.9120377655389457
test_data accuracy 0.8130899937067338

Aqu铆 les dejo mi aporte:

En mi caso, yo use el nombre de la persona, la primera letra del nombre y sacar la ultima letra del primer nombre, por ejemplo:
Jose Mar铆a, la ultima letra seria e.
En caso de solo haber un nombre se saca la ultima letra del mismo, por ejemplo:
Mar铆a, la ultima letra seria a.

con esos tres valores mi precisi贸n subi贸 a 84.08%, lo cual considero que es un predicci贸n aceptable pero estoy consiente que se necesitan mas features para poder entrenar el modelo y tener una mejor predicci贸n. Probablemente agregando la columna con la frecuencia, ya que en algunos casos como el nombre asuncion la frecuencia es major en mujeres que en hombres.

import pandas as pd

path = "D:/Udacity Natural Language Processing/Curso Platzi - Algoritmo de Clasificacion de Texto/names/"
male = pd.read_csv(path + "male_names.txt", header = 0, encoding='latin-1', delimiter = ',')
female = pd.read_csv(path + "female_names.txt", header = 0, encoding='latin-1', delimiter = ',')

male = male.to_numpy()
female = female.to_numpy()

tagset =  [(male[i][0],"male") for i in range(len(male))] + \
        [(str(female[i][0]),"female") for i in range(len(female))]

random.shuffle(tagset)

# Definicion de atributos relaventes
def atributos(nombre):
    atrib = {}
    nombre_list = nombre.split(" ")
    
    atrib["nombre"] = nombre
    atrib["ultima letra"] = nombre_list[0][-1].lower()
    atrib["primera letra"] = nombre_list[0][0].lower()
    
    return atrib

fset = [(atributos(name), genre) for (name, genre) in tagset]

train, test = train_test_split(fset, test_size = 0.20, random_state=42)

classifer = nltk.NaiveBayesClassifier.train(train)

# performance del modelo
print(nltk.classify.accuracy(classifer, test))

0.8407985407377382

Entrenando con el diccionario en espa帽ol me da estos resultados

Hice varias pruebas y al igual que para el resto, mis mejores resultados fueron tomando la ultima letra y las ultimas cuatro:
test.accuracy: 0.76
train.accuracy: 0.91
dejo mi algoritmo de pre-procesamiento:

import re
patron1 = re.compile('[A-Z]+')

male_names = open("spanish-names-surnames/male_names.csv","r",encoding="utf-8")
mn_list = []
for line in male_names:
  filtrado = patron1.findall(line)
  try:
    mn_list.append((filtrado[0],"male"))
    mn_list.append((filtrado[1],"male"))
  except:
    continue

female_names = open("spanish-names-surnames/female_names.csv","r",encoding="utf-8")
fn_list = []
for line in female_names:
  filtrado = patron1.findall(line)
  try:
    fn_list.append((filtrado[0],"female"))
    fn_list.append((filtrado[1],"female"))
  except:
    continue

n_list = mn_list + fn_list
n_set = set(n_list)
nn_list = list(n_set)
random.shuffle(nn_list)

Se podr铆a hacer un peque帽o cambio al momento de definir los set de prueba y de entrenamiento.

train, test = fset[:500], fset[500:]

En vez de:

train, test = fset[500:], fset[:500]

Y as铆 se tiene un poco mas de control en el entrenamiento y a medida que incluyo mas datos en el set de entrenamiento (tambi茅n debo quitar ese mismo numero del set de prueba) aumento la precisi贸n del modelo.

def atributos_extra(nombre):
  attrib = {}
  attrib['fist_letter'] = nombre[0].lower()
  attrib['ending_letters'] = nombre[-1:-3:-1]
  alphabet = [l for l in 'abcdefghijklmnopqrstuvwxyz']
  for letra in alphabet:
    attrib['count({})'.format(letra)] = nombre.lower().count(letra)
    attrib['has({})'.format(letra)] = (letra in nombre.lower())
  return attrib