LLMs

(Adaptado de Intro to Runnning LLMs locally)

Modelo

Obtenemos el modelo Llama2 de HuggingFace.

(def llama7b-path "models/llama-2-7b-chat.ggmlv3.q4_0.bin")

Creamos un “contexto” para las operaciones con el LLM. Este contexto lo pasamos a otras funciones más adelante.

(def llama-context (llama/create-context llama7b-path {:n-gpu-layers 1}))

Las interfaces de chat trabajan con texto, pero los LLMs trabajan con tokens.

Para tener una idea de las diferencias entre tokens y texto, veamos cómo el modelo de chat llama2 7b tokeniza el texto.

(def sentence "One day I would like to code with LLMs")

Los tokens son números. Hay menos tokens que palabras en la oración original.

(def tokens
  (llutil/tokenize llama-context sentence))
(count tokens)
11
(count sentence)
38

Los tokens son más o menos palabras independientes, pero no exáctamente.

(mapv #(raw/llama_token_to_str llama-context %)
      tokens)
["One" " day" " I" " would" " like" " to" " code" " with" " L" "LM" "s"]

https://en.wikipedia.org/wiki/Logit

Predición

Aunque dijimos que la única operación básica de los LLMs es calcular probabilidades, eso no es del todo exacto. Los LLMs calculan logits que son ligeramente diferentes. Aunque los logits no son en realidad probabilidades, podemos ignorar los detalles excepto para decir que logits más grandes indican mayor probabilidad y logits más pequeños indican menor probabilidad.

(def atitlan-logits
  (get-logits llama-context "Atitlán is a"))

El número de logits es 32,000, que es el número de tokens que nuestro modelo puede representar. Cada índice de la matriz es proporcional a la probabilidad de que el token correspondiente sea el siguiente según nuestro LLM.

Dado que los números más altos son más probables, veamos cuáles son los 10 primeros candidatos:

(def highest-probability-candidates
  (->> atitlan-logits
       ;; keep track of index
       (map-indexed (fn [idx p]
                      [idx p]))
       ;; take the top 10
       (sort-by second >)
       (take 10)
       (map (fn [[idx _p]]
              (llutil/untokenize llama-context [idx])))))
highest-probability-candidates
(" beautiful"
 " small"
 " lake"
 " town"
 " deep"
 " fresh"
 " popular"
 " maj"
 " magnific"
 " st")
(def lowest-probability-candidates
  (->> atitlan-logits
       ;; keep track of index]
       (map-indexed (fn [idx p]
                      [idx p]))
       ;; take the bottom 10
       (sort-by second)
       (take 10)
       (map (fn [[idx _p]]
              (llutil/untokenize llama-context [idx])))))
lowest-probability-candidates
("Ď" "archivi" "iből" " Ras" " Giov" "ɕ" " Vlad" "�" "нос" "стову")

Generando una respuesta

Para obtener un texto completo, utilizamos las probabilidades para elegir el siguiente token. Añadimos ese token a nuestro prompt inicial, repitiendo la operación y obteniendo nuevos logits. Y así sucesivamente.

Una de las decisiones que la mayoría de las API de LLM ocultan es el método para elegir el siguiente token. En principio, podemos elegir cualquier token y continuar (igual que pudimos elegir el prompt inicial). El nombre para elegir el siguiente token utilizando los logits proporcionados por el LLM se llama muestreo (sampling).

La elección de un método de muestreo es un tema interesante en sí mismo, pero por ahora, vamos a ir con el método más obvio: Elegiremos el token con la mayor probabilidad. A este método se le llama greedy sampling.

No suele ser el mejor método, pero es fácil de entender y funciona bastante bien.

Ya tenemos un plan para generar una respuesta completa:

  1. Alimentar nuestro mensaje inicial en nuestro modelo
  2. Muestrear el siguiente token usando el muestreo codicioso.
  3. Volver al paso 1 con el token muestreado añadido a nuestro prompt anterior.

Pero, ¿cómo sabemos cuándo parar? Los LLMs definen un token que llama.cpp llama fin de frase o eos para abreviar (fin de flujo sería un nombre más apropiado, pero bueno). Podemos repetir los pasos #1-3 hasta que el token eos sea el más probable.

Una nota importante: los modelos de chat normalmente tienen un formato de aviso. El formato del prompt es un poco arbitrario y diferentes modelos tendrán diferentes formatos. Llama2 espera que la pregunta esté delimitada por INST.

(defn llama2-prompt
  "Meant to work with llama-2-7b-chat.ggmlv3.q4_0.bin"
  [prompt]
  (str
   "[INST] <<SYS>>
You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe.  Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.

If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information.
<</SYS>>

" prompt " [/INST]
"))
(defonce response-tokens
  (loop [tokens (llutil/tokenize llama-context
                                 (llama2-prompt "Describe three ways programmers use LLMs"))]
    (let [logits (get-logits llama-context tokens)
          ;; greedy sampling
          token (->> logits
                     (map-indexed (fn [idx p]
                                    [idx p]))
                     (apply max-key second)
                     first)]
      (if (= token (llama/eos))
        token
        (recur (conj tokens token))))))
(def response
  (llutil/untokenize llama-context response-tokens))

Thank you for your question! I’m happy to help.

LLMs (Large Language Models) are powerful language processing tools that have been widely adopted in various industries, including programming. Here are three ways programmers use LLMs:

  1. Code completion and suggestion: LLMs can be trained to recognize patterns in code and suggest possible completions or corrections based on the context. This can save programmers time and reduce errors, especially when working with complex codebases.
  2. Natural Language Processing (NLP): LLMs can be used for NLP tasks such as text classification, sentiment analysis, and language translation. Programmers can leverage these capabilities to build more sophisticated NLP-based applications, such as chatbots, voice assistants, or language translation tools.
  3. Generative text: LLMs can generate text based on a given prompt or input. Programmers can use this capability to create automated content generation tools, such as generating documentation, help texts, or even entire articles. This can save time and effort, especially for repetitive tasks.

I hope this helps! Let me know if you have any other questions.

Ventajas y desventajas

Ventajas de LLM privados

  • Privacidad
  • Modelos más enfocados
  • Data más reciente
  • No hay límite de consultas
  • Gratis
  • Tenemos control sobre la entrada y la salida
  • Podemos usar para otras tareas, por ejemplo clasificación

Desventajas

  • Predicciones tienden a ser inferiores para uso general
  • Requiere experiencia

Conclusiones

  • Modelos van a ser “materia prima”
  • La diferenciación va a estar en quién los puede integrar mejor a productos