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:
text: Un vector de caracteres de textos a anotar.
prompt: Cadena que define la tarea o pregunta principal que se transmitirá al modelo lingüístico.
template: Una plantilla de cadena para dar formato a las consultas del usuario, que contiene marcadores de posición como {text}, {prefix} y {suffix}.
system: Una cadena opcional para especificar una pregunta del sistema.
prefix: Una cadena prefijo para anteponer a cada consulta de usuario.
sufix: Una cadena sufijo para añadir a cada consulta de usuario.
examples: Un tibble con las columnas
text y answer, que representan ejemplos de
mensajes de usuario y las correspondientes respuestas del
asistente.
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.
mirostat-tau: Representa la perplejidad objetivo que Mirostat intenta mantener durante la generación de texto. La perplejidad es una medida de incertidumbre o sorpresa en el texto generado.
Valores bajos (\(2\) - \(5\)): Generan texto más predecible y consistente. Útil para tareas como explicaciones técnicas o resúmenes formales.
Valores medios (\(6\) - \(8\)): Balancea entre coherencia y creatividad. Ideal para conversaciones y narrativas generales.
Valores altos (\(9\) - \(15\)): Más creatividad y diversidad, pero puede perder coherencia. Recomendado para escritura creativa como poesía o generación de ideas.
mirostat-eta: Representa la velocidad de ajuste con la que Mirostat modifica las probabilidades de selección para alcanzar el objetivo de perplejidad definido por tau. Es una especie de “tasa de aprendizaje” en el algoritmo.
Valores bajos (\(0.1\) - \(0.3\)): Ajuste lento y estable hacia la perplejidad objetivo. Puede ser útil para mantener una alta coherencia en textos largos.
Valores medios (\(0.4\) - \(0.6\)): Ajuste moderado, equilibrando estabilidad y reactividad. Recomendado para la mayoría de los usos generales.
Valores altos (\(0.7\) - \(1.0\)): Ajuste rápido hacia la perplejidad objetivo. Útil para textos dinámicos donde se busca reaccionar rápidamente a cambios en el contexto.
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 |
num_predict: Es el número de palabras que se le indica prediga. Si es ilimitado, se coloca \(-1\).
temperatura: Cuando el modelo genera un texto, asigna probabilidades a las palabras posibles que pueden seguir en una secuencia. La temperatura modifica estas probabilidades antes de seleccionar la siguiente palabra:
Si temperatura = \(1\) (valor por defecto), las probabilidades no se modifican.
Si temperatura < \(1\), se favorecen las palabras con mayor probabilidad, lo que hace que las respuestas sean más determinísticas y menos creativas.
Si temperatura > \(1\), se aumentan las probabilidades de las palabras menos comunes, haciendo las respuestas más creativas, y aleatorias.
repeat_penalty: Desincentiva que el modelo repita palabras o secuencias específicas, ajustando las probabilidades de las palabras que ya han aparecido en el texto generado.
presence_penalty: Penaliza palabras en función de si ya han aparecido en el texto, pero con un enfoque más ligero y dinámico, incentivando la diversidad sin eliminar por completo la posibilidad de repetición.
frequency_penalty: Mientras que presence_penalty penaliza la presencia de palabras ya utilizadas, frequency_penalty penaliza la frecuencia con la que se repiten esas palabras en el texto.
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 |
penalize_new_line: Penaliza o desincentiva que el modelo genere un salto de línea cuando evalúa las posibles palabras o tokens que pueden seguir. Si se activa, reduce la probabilidad de que se generen saltos de línea repetidos o innecesarios, promoviendo texto más continuo. Si se desactiva, el modelo es libre de insertar saltos de línea sin restricciones, lo que puede ser útil en tareas como generación de listas, poesía, o texto formateado.
lvram: Se utiliza para optimizar el uso de memoria de la GPU en dispositivos con recursos limitados de VRAM (memoria de video). Este ajuste permite ejecutar modelos grandes en hardware menos potente o reducir la carga de memoria para evitar cuellos de botella. La opción TRUE puede aumentar la latencia, ya que requiere más operaciones de carga/descarga desde la RAM o el almacenamiento, y, en algunos casos, la generación de texto puede ser más lenta. Pero habilita la ejecución de modelos grandes en GPUs con poca memoria (por ejemplo, \(4\)-\(8\) GB) y/o reduce el riesgo de errores por falta de memoria (out of memory).
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.
top_k: Limita la selección del próximo token a los k tokens más probables. Con valores bajos (e.g., k=\(10\)), la generación es más coherente y predecible, pero menos diversa. Con valores altos (e.g., k=\(50\)-\(100\)), permite más opciones, incrementando la creatividad y la diversidad, aunque con mayor riesgo de incoherencias.
top_p: Es similar a top-k, pero en
lugar de un número fijo, selecciona tokens cuya suma de probabilidades
acumuladas alcanza un porcentaje (p). Con
p=0.9, el modelo considera solo los tokens con una
probabilidad acumulada del \(90\)%. Con
valores bajos (e.g., \(p=0.7\)), reduce
las opciones a tokens altamente probables, generando texto más
coherente. Con valores altos (e.g., \(p=0.95\)), permite más opciones, aumentando
la creatividad, pero con más riesgo de errores.
min_p: Garantiza que solo se seleccionen tokens
con una probabilidad mínima específica (p). Si un token no
alcanza esta probabilidad mínima, no se considera, aunque sea el único
disponible. Evita que el modelo seleccione tokens extremadamente
improbables y puede forzar coherencia en contextos donde los tokens
menos probables suelen ser errores.
tfs_z (Token Frequency Selection): Modifica el muestreo para priorizar tokens menos frecuentes pero plausibles. Es similar a top-p, pero favorece una diversidad controlada. Ajusta dinámicamente la probabilidad de los tokens según su frecuencia histórica y la distribución actual. Es útil para evitar texto repetitivo, pero con mayor control que top-p.
typical_p: Selecciona tokens que son “típicos” o más representativos en el contexto actual. Mide la entropía de un token (qué tan esperado es en relación con el contexto) y favorece aquellos que se alinean mejor con la distribución general. Con valores bajos (e.g., \(p=0.2\)), el modelo selecciona opciones más “normales” o predecibles, sacrificando creatividad. Con valores altos (e.g., \(p=0.8\)), genera texto más variado y creativo, con cierto grado de coherencia.
repeat_last_n: Determina cuántos tokens previos
se consideran para aplicar penalizaciones por repetición. Por ejemplo,
con repeat_last_n=50, el modelo penaliza la repetición de
palabras o frases en las últimas \(50\)
tokens. Ayuda a evitar que el modelo entre en bucles o repita fragmentos
de texto recientes. Con valores bajos (e.g., \(n=10\)), penaliza repeticiones en un rango
pequeño, útil para texto continuo. Con valores altos (e.g., \(n=100\)), penaliza repeticiones en
contextos más largos, ideal para evitar redundancia en texto
extenso.
| 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 |
numa (Non-Uniform Memory Access): Controla si el modelo utiliza arquitecturas de memoria NUMA, donde diferentes núcleos de CPU tienen acceso preferente a ciertas regiones de memoria. Es común en sistemas con múltiples procesadores o nodos (servidores de alto rendimiento). Activarlo permite que el modelo se optimice para estas arquitecturas, reduciendo la latencia en el acceso a la memoria. Si se desactiva, el modelo trata la memoria como un recurso uniforme, lo que puede ser menos eficiente en sistemas NUMA. En sistemas no-NUMA (e.g., una sola CPU) déjelo desactivado.
num_ctx (Número de Tokens en el Contexto): Define cuántos tokens de contexto puede manejar el modelo simultáneamente. Es el tamaño máximo de la “ventana de contexto” en cada generación de texto. Valores más altos permiten manejar textos más largos y recordar más contexto, pero requieren más memoria y tiempo de cómputo. Valores más bajos reducen la carga de memoria, pero pueden limitar la coherencia en texto extenso. Para GPUs con poca VRAM ajustélo a \(512\) o menos. Con GPUs potentes, ajústelo a valores mayores (\(1024–4096\)) para texto largo.
num_batch: Controla cuántas solicitudes de
generación o tokens se procesan simultáneamente. Afecta el rendimiento y
la eficiencia durante la inferencia o entrenamiento. Tamaño de lote
pequeño requiere menor uso de memoria, pero se vuelve más lento. Con
tamaño de lote grande otorga mayor velocidad, pero requiere más memoria.
En hardware con poca VRAM o RAM ajústelo a valores bajos (e.g.,
num_batch=1 o num_batch=2. En sistemas
robustos, sea \(8\).
num_gpu: Define cuántas GPUs se utilizarán para cargar y procesar el modelo. Usar múltiples GPUs distribuye la carga computacional, permitiendo manejar modelos más grandes y tareas más complejas. Usar una sola GPU es más sencillo pero puede limitar el tamaño del modelo o la velocidad de generación.
main_gpu: En donde las GPUs tengan diferentes capacidades, permite especificar qué GPU será la principal. main_gpu=\(0\) para la primera GPU.
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 |
vocab_only: Indica si el modelo debe cargar y trabajar únicamente con el vocabulario y no con el resto de los pesos o capas del modelo. Es útil para operaciones que solo requieren información sobre las palabras o tokens, como la tokenización o evaluación básica del vocabulario. Si es activado reduce drásticamente el uso de memoria, ya que solo carga el vocabulario. Y es ideal para tareas ligeras relacionadas con la estructura del vocabulario, sin necesidad de realizar inferencia completa. Por tanto, para tokenización y análisis del vocabulario es bueno que esté activado. De otra manera no.
use_mmap: Habilita el uso de memory mapping (mmap) para cargar los pesos del modelo desde el disco directamente a la memoria virtual, en lugar de cargarlos completamente en la RAM. Permite manejar modelos grandes incluso en sistemas con RAM limitada.
use_mlock: Bloquea los pesos del modelo en la
RAM, evitando que sean intercambiados al almacenamiento secundario
(swap) por el sistema operativo. Esto garantiza que los datos del modelo
estén siempre disponibles en memoria, mejorando la velocidad y la
estabilidad. use_mlock=True para maximizar el rendimiento.
use_mlock=False para evitar errores de memoria.
| 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 |