Predecir género a partir de nombres usando un modelo de lenguaje en R

11/11/2024

Temas: procesamiento de datos inteligencia artificial análisis de texto

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 Juan Jesus Herrera Fuentes      
 2 masculino Efrain Oporto Torres            
 3 masculino Dino Lotito Flores              
 4 masculino Maria Marjorie Jopia Herrera    
 5 masculino Jorge Contanzo Bravo            
 6 masculino Cristobal Sanchez Carvallo      
 7 masculino Humberto Garcia Diaz            
 8 masculino Gustavo Diego Marquez Cadagan   
 9 masculino Maximiliano Radonich Radonich   
10 masculino Luis Reinaldo Pradenas Moran    
11 masculino Nelson Fernando Lazcano Silva   
12 masculino Bernardo Cornejo Ceron          
13 masculino Rodrigo Esteban Castro Sepulveda
14 masculino Juan Pablo Gomez Ramirez        
15 masculino Kether Bautista Gomez Pasten    

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 Marcelo Marcelo Patricio Bersano Reyes
2 Americo Americo Guajardo Oyarce       
3 Jorge   Jorge Westermeier Estrada     
4 Omar    Omar Vera Castro              
5 Camilo  Camilo Riffo Quintana         

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.

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:
November 11, 2024
Extensión:
6 minute read, 1176 words
Tags:
procesamiento de datos inteligencia artificial análisis de texto
Ver también:
Contar días hábiles entre dos fechas en R
Comparar el rendimiento expresiones en R
Visualización y scraping de resultados en vivo de las elecciones municipales 2024