Predecir género a partir de nombres usando un modelo de lenguaje en R
19/2/2025
Hace poco conocí
el paquete {mall}, que facilita mucho el uso de un un modelo de lenguaje (LLM) local como una herramienta cotidiana para el análisis y procesamiento de datos.
El paquete incluye varias funciones para usar un modelo LLM local en las columnas de un dataframe. {mall} te puede ayudar a :
- clasificar el contenido de una variable
- resumir textos
- extraer sentimiento a partir del texto
- extraer información desde el texto
- confirmar si algo es verdadero o falso a partir de un texto
- y también a aplicar cualquier prompt a una variable.
Recientemente lo usé para un caso real, donde tenía una columna de casi 2.000 nombres, y necesitaba asignarle un género a cada una de estas personas, solamente a partir de sus nombres y apellidos.
library(dplyr) # manipulación de datos
library(readr) # cargar datos
library(tictoc) # medición de tiempo
library(mall) # aplicar modelos de lenguaje en dataframe
Los datos provienen del servicio electoral de Chile, y los nombres corresponden a 1.576 candidatos y candidatas a alcaldías.
Con el siguiente código puedes descargar los datos listos para su uso:
archivo_remoto <- "https://raw.githubusercontent.com/bastianolea/servel_scraping_votaciones/refs/heads/main/datos/resultados_alcaldes_2024.csv"
candidatos <- readr::read_csv2(archivo_remoto) |>
select(nombres = candidato, partido, sector) |>
filter(nombres != "Nulo/Blanco")
candidatos
# A tibble: 1,576 × 3
nombres partido sector
<chr> <chr> <chr>
1 Marco Antonio Gonzalez Candia RN Derecha
2 Veronica Maricel Cueto Cueto IND Independiente
3 Marcela Maritza Mansilla Potocnjak IND Independiente
4 Alejandro Felipe Ricotti Sepulveda IND Independiente
5 Carlos Orlando Tapia Aviles IND Independiente
6 Maria Luisa Hamilton Velasco IND Independiente
7 Gaston Dubournais Riveros IND Independiente
8 Marcela Chamorro Macias IND Izquierda
9 Jose Andres Arellano Blanco IND Independiente
10 Andrea Elizabeth Galvez Sepulveda REP Derecha
# ℹ 1,566 more rows
En esta instancia, tengo configurado {ollamar} para que use el modelo local y de código abierto
Llama 3.2, de 3 billones de parámetros. Podemos instalar este modelo, o uno distinto, con estas instrucciones:
library(ollamar)
ollamar::pull("llama3.2") # descargar e instalar un modelo
mall::llm_use("ollama", "llama3.2") # elegir un modelo instalado
En una primera prueba, le entregamos al modelo de lenguaje la columna nombres (que contiene nombre, segundo nombre, apellido, y segundo apellido), y le pedimos al modelo que clasifique cada observación como masculino o femenino.
tic()
resultados_1 <- candidatos |>
llm_classify(nombres,
labels = c("masculino", "femenino"),
pred_name = "genero")
Ollama local server running
── mall session object
Backend: Ollama
LLM session: model:llama3.2:latest
R session:
cache_folder:/var/folders/z8/61w5pwts4h5fsvhfqs1wpvk40000gn/T//RtmpntMvSc/_mall_cache127421f0326aa
toc()
421.687 sec elapsed
El proceso tarda aproximadamente 7 minutos en clasificar los casi 1.576 nombres, un ritmo de 0.26 segundos por cada predicción.
resultados_1 |> select(genero, nombres) |> slice_sample(n = 15)
# A tibble: 15 × 2
genero nombres
<chr> <chr>
1 masculino Andres Parra Sandoval
2 femenino Leontina Del Carmen Gutierrez Rivas
3 masculino Mario Ortiz Cabezas
4 masculino Jose Esteban Alarcon Vargas
5 femenino Nancy Milena Alfaro Jurjevic
6 masculino Osvaldo Herrera Valdes
7 femenino Ninoska Villegas Lamas
8 masculino Juan Carlos Diaz Avendaño
9 masculino German Pino Maturana
10 masculino Gonzalo Rubio Fuenzalida
11 femenino Laura Correa Fuentes
12 masculino Alex Fernando Castillo Blas
13 masculino Jonathan Velasquez Ramirez
14 masculino Jorge Espinoza Cuevas
15 femenino Elizabeth Marican Rivas
Para la segunda prueba, intentamos entregarle al modelo solamente los nombres, excluyendo segundos nombres y apellidos, bajo el supuesto de que el primer nombre es el mejor predictor del género, mientras que los apellidos no son un predictor del género.
Probamos el código con una expresión regular (regex) para extraer la primera palabra de una secuencia de texto:
candidatos |>
mutate(nombre = stringr::str_extract(nombres, "\\w+")) |>
select(nombre, nombres) |>
slice_sample(n = 5)
# A tibble: 5 × 2
nombre nombres
<chr> <chr>
1 Paola Paola Romero Valdivia
2 Alejandra Alejandra Villegas Huichaman
3 Yuliana Yuliana Ema Bustos Zapata
4 Elizabeth Elizabeth Salinas Valle
5 Freddy Freddy Antonio Ramirez Villalobos
Realizamos la segunda prueba de clasificación, esta vez solamente con el primer nombre:
tic()
resultados_2 <- candidatos |>
mutate(nombre = stringr::str_extract(nombres, "\\w+")) |> # extraer nombres
llm_classify(nombre,
labels = c("masculino", "femenino"),
pred_name = "genero")
toc()
139.47 sec elapsed
Esta vez el proceso tarda aproximadamente 2 minutos, ¡casi 4 veces más rápido! El modelo clasifica los textos más rápido mientras menos texto tenga que analizar. Es importante mencionar que la velocidad va a depender mucho de tu computador, específicamente su GPU.
resultados_2 |> select(genero, nombre) |> slice_sample(n = 10)
# A tibble: 10 × 2
genero nombre
<chr> <chr>
1 masculino Pablo
2 masculino Luis
3 femenino Sandra
4 masculino Johnny
5 masculino Miguel
6 masculino Oscar
7 masculino Luis
8 masculino Gonzalo
9 masculino Juan
10 masculino Mauricio
Revisemos los resultados, obteniendo una muestra al azar de 30 nombres:
resultados_2 |>
filter(nombres != "Nulo/Blanco") |>
relocate(genero, nombre, .before = nombres) |>
slice_sample(n = 30) |>
print(n = Inf)
# A tibble: 30 × 5
genero nombre nombres partido sector
<chr> <chr> <chr> <chr> <chr>
1 masculino Christian Christian Arturo Cardenas Silva IND Independien…
2 masculino Osvaldo Osvaldo Cartagena Garcia IND Independien…
3 masculino Romelio Romelio Borquez Hidalgo IND Derecha
4 masculino Patricio Patricio Marchant Ulloa IND Centro
5 masculino Claudio Claudio Miranda Ibañez IND Independien…
6 masculino Marcos Marcos Gaete Flores IND Derecha
7 masculino Raul Raul Jaime Espejo Escobar IND Independien…
8 masculino Camilo Camilo Alejandro Rapu Riroroko IND Centro
9 femenino Clara Clara Lazcano Fernández IND Derecha
10 masculino Drazen Drazen Andre Markusovic Caceres PDG Derecha
11 femenino Denisse Denisse Tamara Leon Rojas REP Derecha
12 masculino Juan Juan Pablo Gomez Ramirez IND Independien…
13 masculino Alexis Alexis Mendez Uribe IND Independien…
14 femenino Juana Juana Sarmiento Acosta IND Independien…
15 masculino Marco Marco Antonio Gonzalez Candia RN Derecha
16 masculino Miguel Miguel Bruna Silva IND Independien…
17 masculino Hernaldo Hernaldo Andres Ahumada Chavez IND Independien…
18 femenino Nivia Nivia Del Carmen Riquelme Gutierrez IGUALDAD Izquierda
19 masculino Cristian Cristian Alejandro Sobarzo Sanhueza IND Independien…
20 masculino Cristian Cristian Andres Pozo Parraguez PS Izquierda
21 femenino Rosa Rosa Torres Escobedo IND Independien…
22 masculino Juan Juan Carlos Gonzalez Romo IND Independien…
23 femenino Paula Paula Rodriguez Valenzuela IND Independien…
24 masculino Gabriel Gabriel Silva Gonzalez IND Independien…
25 femenino Daniela Daniela Isabel Munizaga Villegas REP Derecha
26 masculino Ramon Ramon Sandoval Zurita IND Independien…
27 masculino Ramon Ramon Bahamonde Cea IND Independien…
28 masculino Claudio Claudio Arellano Cortes IND Independien…
29 femenino Sigrid Sigrid Ramirez Arias IND Derecha
30 masculino Patricio Patricio Ernesto Gonzalez Nuñez IND Independien…
Revisando los nombres, al parecer las predicciones son bastante buenas, pero al hacer más pruebas nos damos cuenta de que no es 100% infalible, ya que se equivoca en algunos nombres como Aracelli, Edita, Elizabeth, o Josselyn. si se requiere aumentar la calidad de la predicción, podría instalarse un modelo de lenguaje más grande.
Otra alternativa para mejorar el desempeño de la clasificación (es decir, que sea más certer) es agregarle al prompt más contexto sobre lo que se busca obtener, así como una alternativa para que el modelo clasifique términos que no sabe dónde clasificar:
resultados_3 <- candidatos |>
mutate(nombre = stringr::str_extract(nombres, "\\w+")) |> # extraer nombres
llm_classify(nombre,
labels = c("masculino", "femenino", "desconocido"),
pred_name = "genero",
additional_prompt = "obtener el género desde nombres de personas")
En el argumento additional_prompt podemos especifiacr una instrucción extra para el modelo, lo que a veces es suficiente para que el modelo tenga contexto extra para poder clasificar correctamente. En mi experiencia, esto mejora considerablemente la calidad de las respuestas, y reduce la incertidumbre en algunos casos.
En conclusión, se trata de una herramienta muy fácil de implementar, que también es capaz de ahorrarnos bastante tiempo, por ejemplo, en el desarrollo de un algoritmo que detecte el nombre a partir de ciertas estructuras en la composición de cada uno, o bien, en tener que contrastar los nombres con alguna base de datos ya existente de nombres con un género asignado (si es que existe).
- Fecha de publicación:
- February 19, 2025
- Extensión:
- 7 minute read, 1309 words