Existen al menos dos librerías para usar modelos LLM de manera local: rollama y ollamar.

Hacen básicamente lo mismo, así que se presenta sólo la primera: Se instala con install.packages("rollama"). La última versión con remotes::install_github("JBGruber/rollama").

Se hace un llamado usual.

library(rollama)
packageVersion("rollama")
## [1] '0.2.0.9000'

Ollama debe estar activo en el sistema operativo.

Se puede saber realizando un ping:

ping_ollama()
## ▶ Ollama (v0.3.12) is running at <http://localhost:11434>!

Se está listo para bajar a nivel local modelos, por su nombre.

Los nombres de los modelos siguen el formato modelo:etiqueta, en el que el modelo puede tener un espacio de nombres opcional para especificar una versión. Algunos ejemplos son orca-mini:3b-q4_1 y llama3.1:70b. Si no se indica la etiqueta, será por defecto la última versión.

Por defecto, el paquete utiliza el modelo «llama3.1».

pull_model()
## ✔ model llama3.1 pulled succesfully

Otros ejemplos:

pull_model("llama3")
pull_model("aya-expanse")

Se pueden llamar también especificando la ubicación en https://ollama.com/library

pull_model("https://huggingface.co/Snowflake/snowflake-arctic-embed-m-v1.5:BF16")

Para conocer la lista de modelos disponibles a nivel local:

list_models()
## # A tibble: 2 × 11
##   name       model modified_at   size digest parent_model format family families
##   <chr>      <chr> <chr>        <dbl> <chr>  <chr>        <chr>  <chr>  <list>  
## 1 llama3.1:… llam… 2024-12-12… 4.92e9 46e0c… ""           gguf   llama  <chr>   
## 2 aya-expan… aya-… 2024-12-12… 5.06e9 65f98… ""           gguf   comma… <chr>   
## # ℹ 2 more variables: parameter_size <chr>, quantization_level <chr>

Para conocer el modelo activo:

show_model()
## # A tibble: 1 × 39
##   license      modelfile parameters template parent_model format family families
##   <chr>        <chr>     <chr>      <chr>    <chr>        <chr>  <chr>  <list>  
## 1 "LLAMA 3.1 … "# Model… "stop    … "{{- if… ""           gguf   llama  <chr>   
## # ℹ 31 more variables: parameter_size <chr>, quantization_level <chr>,
## #   general.architecture <chr>, general.basename <chr>,
## #   general.file_type <int>, general.finetune <chr>, general.languages <list>,
## #   general.license <chr>, general.parameter_count <dbl>,
## #   general.quantization_version <int>, general.size_label <chr>,
## #   general.tags <list>, general.type <chr>, llama.attention.head_count <int>,
## #   llama.attention.head_count_kv <int>, …
# # Para ver completo
# library(magrittr)
# cat(show_model() %>% unlist())

De hecho, el modelo por defecto, como ya se mencionó es el llama3.1. Para fijar algún otro como activo:

options(rollama_model = "aya-expanse")
options(rollama_model = "llama3.1")

Para cambiar algún nombre:

copy_model("huggingface.co/Snowflake/snowflake-arctic-embed-m-v1.5:BF16", 
           destination = "snowflake-artic")

Para borrar alguno no deseado:

delete_model("huggingface.co/Snowflake/snowflake-arctic-embed-m-v1.5:BF16")

En general, se hacen preguntas de manera interactiva con la función query():

query(q = "Dime un refrán relacionado con el buen humor. Sólo uno. No me expliques su interpretación", screen = TRUE, output = "text", 
      model_params = list(seed = 42))
## 
## ── Answer from llama3.1 ────────────────────────────────────────────────────────
## No hay mal que dure cien años, ni cristal que pueda romperse.

Otros parámetros que puede haber son:

query("Dime un refrán relacionado con el buen humor. Sólo uno. No me expliques su interpretación", model = "llama3.1",
      model_params = list(num_keep = 5,
                          seed = 42,        
                          num_predict = 100,
                          top_k = 20,
                          top_p = 0.9,
                          min_p = 0.0,
                          tfs_z = 0.5,
                          typical_p = 0.7,
                          repeat_last_n = 33,
                          temperature = 0.8,
                          repeat_penalty = 1.2,
                          presence_penalty = 1.5,
                          frequency_penalty = 1.0,
                          mirostat = 1,
                          mirostat_tau = 0.8,
                          mirostat_eta = 0.6,
                          penalize_newline = TRUE,
                          numa = FALSE,
                          num_ctx = 1024,
                          num_batch = 2,
                          num_gpu = 0,
                          main_gpu = 0,
                          low_vram = FALSE,
                          vocab_only = FALSE,
                          use_mmap = TRUE,
                          use_mlock = FALSE,
                          num_thread = 8))
resp <- query(q = "Dime un refrán relacionado con el buen humor. Sólo uno. No me expliques su interpretación", screen = F, verbose = F)
answer <- resp[[1]]$message$content

Pero se puede crear un esquema de preguntas mediante make_query(). Sus parámetros son:

Ejemplo:

plantilla <- "{prefix}{text}\n\n{prompt}{suffix}"
ejemplos <- tibble::tribble(~text, ~answer,
  "Esta película fue increíble, con gran actuación y la historia.", "positivo",
  "La película estuvo bien, pero no fue particularmente memorable.", "neutral",
  "Esta película me pareció aburrida y mal hecha.", "negativo")
preguntas <- make_query(text = c("Un espectáculo visual impresionante.", 
                               "Predecible pero bien actuada."),
                      prompt = "Clasifica el sentimiento como positivo, neutral o negativo.",
                      template = plantilla,
                      system = "Proporcione una clasificación de sentimientos.",
                      prefix = "Reseña: ",  suffix = " Por favor, clasifica.",
                      examples = ejemplos)

Para ejecutar la consulta:

if (ping_ollama()) {
  query(preguntas, screen = TRUE, output = "text")
}

La semilla de la reproducibilidad se puede fijar de antemano:

options(rollama_seed = 42)

Formular la misma pregunta a varios modelos también es posible:

query("Dime un refrán relacionado con el buen humor. Sólo uno. No me expliques su interpretación", model = c("llama3.1", "aya-expanse"))
## 
## ── Answer from llama3.1 ────────────────────────────────────────────────────────
## No hay mal que dure cien años, ni cristal que pueda romperse.
## 
## ── Answer from aya-expanse ─────────────────────────────────────────────────────
## "Ríe y serás más feliz."

Una característica de query es que no tiene memoria de la conversación.

Para mantenerla se hace uso de la función chat():

chat(q = "Dime un refrán relacionado con el buen humor. Sólo uno. No me expliques su interpretación")
## 
## ── Answer from llama3.1 ────────────────────────────────────────────────────────
## No hay mal que dure cien años, ni cristal que pueda romperse.
chat("Dame la interpretación")
## 
## ── Answer from llama3.1 ────────────────────────────────────────────────────────
## Es un refrán que indica que incluso los problemas más grandes son temporales y
## pasan con el tiempo. Además, sugiere que también las cosas más frágiles pueden
## resistir el paso del tiempo.
## 
## En otras palabras, no hay problema que sea definitivo o inamovible. Algo bueno
## siempre es esperar un cambio para la mejoría.

Se restablece la memoria a cero mediante new_chat()

Se puede revisar la historia del chat mediante chat_history() .

Es importante entender los parámetros:

Normalmente existen dos versiones de Mirostat:

- Mirostat $1$: La implementación original, que ofrece un mecanismo de ajuste sencillo.

- Mirostat $2$: Una versión refinada que introduce un parámetro de velocidad de aprendizaje, ofreciendo más estabilidad y ajustes más suaves.

Configuraciones típicas según el propósito:

Propósito tau eta
Textos técnicos o formales 5 0.2
Narrativa y descripciones 7 0.5
Textos creativos (historias, poesía) 10 0.7
Evitar bucles o redundancia extrema 12 0.9

Configuraciones típicas según el propósito:

Propósito repeat_penalty presence_penalty frequency_penalty
Textos técnicos o formales 1.2 0.3 0.2
Narrativa y descripciones 1.3 0.5 0.8
Textos creativos (historias, poesía) 1.4 0.7 1.5
Evitar bucles o redundancia extrema 1.5 0.6 2.0

Estos parámetros son controles avanzados que ajustan cómo los modelos de lenguaje eligen la próxima palabra o token en el texto generado. Cada uno afecta diferentes aspectos del proceso de muestreo, y combinarlos puede ayudarte a lograr texto más coherente, diverso o ajustado a tus necesidades específicas.

Propósito top_k top_p repeat_last_n typical_p min_p
Texto técnico o formal 10 0.7 50
Narrativa general 40 0.9 30
Texto creativo (historias, poesía) 100 0.95 10
Evitar bucles o redundancia 100 0.2 0.1

Combinaciones comunes para optimización:

Tipo numa num_ctx num_batch num_gpu main_gpu
Sistemas con una sola GPU False 1024 1 1 0
Sistemas con múltiples GPUs True 2048 4 2 0
Hardware con poca VRAM False 512 1 1 0
Tipo vocab_only use_mmap use_mlock
Sistemas con poca RAM (\(\le16\) GB) FALSE TRUE FALSE
Sistemas con suficiente RAM (\(\ge32\) GB) FALSE FALSE TRUE
Tareas relacionadas solo con vocabulario TRUE FALSE FALSE