1r Fwno Svyymm Tepiovuw.jpeg

Aprenda conocimientos críticos para crear aplicaciones de IA, en un lenguaje sencillo

La recuperación de generación aumentada, o RAG, está de moda en estos días porque introduce algunas capacidades importantes a modelos de lenguaje grandes como el GPT-4 de OpenAI, y esa es la capacidad de usar y aprovechar sus propios datos.

Esta publicación le enseñará la intuición fundamental detrás de RAG y al mismo tiempo le brindará un tutorial simple para ayudarlo a comenzar.

Hay mucho ruido en el espacio de la IA y en particular sobre RAG. Los vendedores están tratando de complicarlo demasiado. Están intentando inyectar sus herramientas, sus ecosistemas, su visión.

Está haciendo que RAG sea mucho más complicado de lo necesario. Este tutorial está diseñado para ayudar a los principiantes a aprender cómo crear aplicaciones RAG desde cero. Sin tonterías, sin jerga (vale, mínima), sin bibliotecas, solo una sencilla aplicación RAG paso a paso.

Jerry de LlamaIndex aboga por construir cosas desde cero para entender realmente las piezas. Una vez que lo hagas, usar una biblioteca como LlamaIndex tiene más sentido.

Construya desde cero para aprender y luego construya con bibliotecas para escalar.

¡Empecemos!

Es posible que haya oído hablar o no de Retrieval Augmented Generation o RAG.

Aquí está la definición de la publicación del blog que presenta el concepto de Facebook:

Construir un modelo que investigue y contextualice es más desafiante, pero es esencial para avances futuros. Recientemente logramos un progreso sustancial en este ámbito con nuestra arquitectura de recuperación de generación aumentada (RAG), un modelo diferenciable de extremo a extremo que combina un componente de recuperación de información (el sistema de recuperación de pasajes densos de Facebook AI) con un generador seq2seq (nuestro sistema bidireccional y automático). -Transformadores regresivos [BART] modelo). RAG se puede ajustar en tareas posteriores que requieren un uso intensivo de conocimiento para lograr resultados de última generación en comparación incluso con los modelos de lenguaje seq2seq previamente entrenados más grandes. Y a diferencia de estos modelos previamente entrenados, el conocimiento interno de RAG se puede alterar fácilmente o incluso complementar sobre la marcha, lo que permite a los investigadores e ingenieros controlar lo que RAG sabe y no sabe sin perder tiempo ni potencia de cálculo reentrenando todo el modelo.

Vaya, eso es un bocado.

Al simplificar la técnica para principiantes, podemos afirmar que la esencia de RAG implica agregar sus propios datos (a través de una herramienta de recuperación) al mensaje que pasa a un modelo de lenguaje grande. Como resultado, obtienes un resultado. Eso te brinda varios beneficios:

  1. Puede incluir datos en la indicación para ayudar al LLM a evitar alucinaciones.
  2. Puede consultar (manualmente) fuentes de verdad al responder a la consulta de un usuario, lo que ayuda a verificar cualquier problema potencial.
  3. Puede aprovechar datos en los que es posible que el LLM no haya recibido capacitación.
  1. una colección de documentos (formalmente llamada corpus)
  2. Una entrada del usuario.
  3. una medida de similitud entre la recopilación de documentos y la entrada del usuario

Sí, es así de simple.

Para empezar a aprender y comprender los sistemas basados ​​en RAG, no necesitas un almacén de vectores, ni siquiera necesidad un LLM (al menos para aprender y comprender conceptualmente).

Si bien a menudo se presenta como complicado, no tiene por qué serlo.

Realizaremos los siguientes pasos en secuencia.

  1. Recibir una entrada de usuario
  2. Realizar nuestra medida de similitud
  3. Postprocesar la entrada del usuario y los documentos obtenidos.

El posprocesamiento se realiza con un LLM.

El papel RAG real es obviamente el recurso. El problema es que supone MUCHO contexto. Es más complicado de lo que necesitamos.

Por ejemplo, aquí está la descripción general del sistema RAG propuesto en el documento.

Una descripción general de RAG a partir del documento RAG por Lewis, et al.

Eso es denso.

Es fantástico para los investigadores, pero para el resto de nosotros será mucho más fácil aprender paso a paso construyendo el sistema nosotros mismos.

Volvamos a construir RAG desde cero, paso a paso. Estos son los pasos simplificados en los que trabajaremos. Si bien esto no es técnicamente «RAG», es un buen modelo simplificado con el que aprender y permitirnos progresar hacia variaciones más complicadas.

A continuación puede ver que tenemos un corpus simple de ‘documentos’ (sea generoso 😉).

corpus_of_documents = [
"Take a leisurely walk in the park and enjoy the fresh air.",
"Visit a local museum and discover something new.",
"Attend a live music concert and feel the rhythm.",
"Go for a hike and admire the natural scenery.",
"Have a picnic with friends and share some laughs.",
"Explore a new cuisine by dining at an ethnic restaurant.",
"Take a yoga class and stretch your body and mind.",
"Join a local sports league and enjoy some friendly competition.",
"Attend a workshop or lecture on a topic you're interested in.",
"Visit an amusement park and ride the roller coasters."
]

Ahora necesitamos una forma de medir la similitud entre las entrada del usuario vamos a recibir y el recopilación de documentos que organizamos. Podría decirse que la medida de similitud más simple es similitud jaccard. He escrito sobre eso en el pasado (ver esta publicación pero la respuesta corta es que el similitud jaccard es la intersección dividida por la unión de los “conjuntos” de palabras.

Esto nos permite comparar las aportaciones de nuestros usuarios con los documentos originales.

Nota al margen: preprocesamiento

Un desafío es que si tenemos una cadena simple como "Take a leisurely walk in the park and enjoy the fresh air.",, tendremos que preprocesarlo en un conjunto para poder realizar estas comparaciones. Vamos a hacer esto de la forma más sencilla posible, en minúsculas y dividido por " ".

def jaccard_similarity(query, document):
query = query.lower().split(" ")
document = document.lower().split(" ")
intersection = set(query).intersection(set(document))
union = set(query).union(set(document))
return len(intersection)/len(union)

Ahora necesitamos definir una función que tome la consulta exacta y nuestro corpus y seleccione el «mejor» documento para devolverlo al usuario.

def return_response(query, corpus):
similarities = []
for doc in corpus:
similarity = jaccard_similarity(query, doc)
similarities.append(similarity)
return corpus_of_documents[similarities.index(max(similarities))]

Ahora que podemos ejecutarlo, comenzaremos con un mensaje simple.

user_prompt = "What is a leisure activity that you like?"

Y una simple entrada del usuario…

user_input = "I like to hike"

Ahora podemos devolver nuestra respuesta.

return_response(user_input, corpus_of_documents)
'Go for a hike and admire the natural scenery.'

Felicitaciones, ha creado una aplicación RAG básica.

Tengo 99 problemas y uno de ellos es el de mala similitud.

Ahora hemos optado por una medida de similitud simple para el aprendizaje. Pero esto va a ser problemático porque es muy simple. No tiene noción de semántica. Se trata simplemente de ver qué palabras hay en ambos documentos. Eso significa que si proporcionamos un ejemplo negativo, obtendremos el mismo «resultado» porque ese es el documento más cercano.

user_input = "I don't like to hike"
return_response(user_input, corpus_of_documents)
'Go for a hike and admire the natural scenery.'

Este es un tema que surgirá mucho con “RAG”, pero por ahora, tenga la seguridad de que abordaremos este problema más adelante.

En este punto, no hemos realizado ningún posprocesamiento del “documento” al que respondemos. Hasta ahora, hemos implementado sólo la parte de «recuperación» de la «Generación aumentada de recuperación». El siguiente paso es aumentar la generación incorporando un modelo de lenguaje grande (LLM).

Para hacer esto, vamos a usar ollama para poner en marcha un LLM de código abierto en nuestra máquina local. Podríamos usar fácilmente gpt-4 de OpenAI o Claude de Anthropic, pero por ahora comenzaremos con el código abierto llama2 de Meta IA.

Esta publicación asumirá algunos conocimientos básicos de modelos de lenguaje grandes, así que comencemos a consultar este modelo.

import requests
import json

Primero vamos a definir las entradas. Para trabajar con este modelo, vamos a tomar

  1. entrada del usuario,
  2. buscar el documento más similar (según lo medido por nuestra medida de similitud),
  3. pasar eso a un mensaje al modelo de lenguaje,
  4. entonces devolver el resultado al usuario

Esto introduce un nuevo término, el inmediato. En resumen, son las instrucciones que usted proporciona al LLM.

Cuando ejecute este código, verá el resultado de la transmisión. La transmisión es importante para la experiencia del usuario.

user_input = "I like to hike"
relevant_document = return_response(user_input, corpus_of_documents)
full_response = []
prompt = """
You are a bot that makes recommendations for activities. You answer in very short sentences and do not include extra information.
This is the recommended activity: {relevant_document}
The user input is: {user_input}
Compile a recommendation to the user based on the recommended activity and the user input.
"""

Habiendo definido eso, ahora hagamos la llamada API a ollama (y llama2). un paso importante es asegurarse de que ollama ya se esté ejecutando en su máquina local ejecutando ollama serve.

Nota: esto puede ser lento en su máquina, ciertamente lo es en la mía. Ten paciencia, joven saltamontes.

url = 'http://localhost:11434/api/generate'
data = {
"model": "llama2",
"prompt": prompt.format(user_input=user_input, relevant_document=relevant_document)
}
headers = {'Content-Type': 'application/json'}
response = requests.post(url, data=json.dumps(data), headers=headers, stream=True)
try:
count = 0
for line in response.iter_lines():
# filter out keep-alive new lines
# count += 1
# if count % 5== 0:
# print(decoded_line['response']) # print every fifth token
if line:
decoded_line = json.loads(line.decode('utf-8'))

full_response.append(decoded_line['response'])
finally:
response.close()
print(''.join(full_response))

Great! Based on your interest in hiking, I recommend trying out the nearby trails for a challenging and rewarding experience with breathtaking views Great! Based on your interest in hiking, I recommend checking out the nearby trails for a fun and challenging adventure.

Esto nos brinda una Aplicación RAG completa, desde cero, sin proveedores, sin servicios. Conoce todos los componentes de una aplicación de recuperación-generación aumentada. Visualmente, esto es lo que hemos construido.

El LLM (si tiene suerte) manejará la entrada del usuario que vaya en contra del documento recomendado. Eso lo podemos ver a continuación.

user_input = "I don't like to hike"
relevant_document = return_response(user_input, corpus_of_documents)
# https://github.com/jmorganca/ollama/blob/main/docs/api.md
full_response = []
prompt = """
You are a bot that makes recommendations for activities. You answer in very short sentences and do not include extra information.
This is the recommended activity: {relevant_document}
The user input is: {user_input}
Compile a recommendation to the user based on the recommended activity and the user input.
"""
url = 'http://localhost:11434/api/generate'
data = {
"model": "llama2",
"prompt": prompt.format(user_input=user_input, relevant_document=relevant_document)
}
headers = {'Content-Type': 'application/json'}
response = requests.post(url, data=json.dumps(data), headers=headers, stream=True)
try:
for line in response.iter_lines():
# filter out keep-alive new lines
if line:
decoded_line = json.loads(line.decode('utf-8'))
# print(decoded_line['response']) # uncomment to results, token by token
full_response.append(decoded_line['response'])
finally:
response.close()
print(''.join(full_response))
Sure, here is my response:

Try kayaking instead! It's a great way to enjoy nature without having to hike.

Si volvemos a nuestro diagrama de la aplicación RAG y pensamos en lo que acabamos de crear, veremos varias oportunidades de mejora. En estas oportunidades es donde intervienen herramientas como almacenes de vectores, incrustaciones y «ingeniería» rápida.

Aquí hay diez áreas potenciales donde podríamos mejorar la configuración actual:

  1. El número de documentos 👉 más documentos pueden significar más recomendaciones.
  2. La profundidad/tamaño de los documentos. 👉 Podría ser mejor un contenido de mayor calidad y documentos más largos con más información.
  3. La cantidad de documentos que entregamos al LLM. 👉 En este momento, solo le damos al LLM un documento. Podríamos introducir varios como «contexto» y permitir que el modelo proporcione una recomendación más personalizada basada en la entrada del usuario.
  4. Las partes de los documentos que entregamos al LLM. 👉 Si tenemos documentos más grandes o más completos, es posible que simplemente queramos agregar partes de esos documentos, partes de varios documentos o alguna variación de los mismos. En el léxico, esto se llama fragmentación.
  5. Nuestra herramienta de almacenamiento de documentos 👉 Es posible que almacenemos nuestros documentos de una forma diferente o en una base de datos diferente. En particular, si tenemos muchos documentos, podríamos explorar almacenarlos en un lago de datos o en un almacén de vectores.
  6. La medida de similitud 👉 La forma en que medimos la similitud es importante; es posible que debamos equilibrar el rendimiento y la minuciosidad (por ejemplo, examinar cada documento individual).
  7. El preprocesamiento de los documentos y la entrada del usuario. 👉 Podríamos realizar algún preprocesamiento adicional o aumento de la entrada del usuario antes de pasarla a la medida de similitud. Por ejemplo, podríamos usar una incrustación para convertir esa entrada en un vector.
  8. La medida de similitud 👉 Podemos cambiar la medida de similitud para obtener documentos mejores o más relevantes.
  9. El modelo 👉 Podemos cambiar el modelo final que utilicemos. Estamos usando llama2 arriba, pero podríamos usar fácilmente un modelo Antrópico o Claude.
  10. el aviso 👉 Podríamos usar un mensaje diferente en LLM/Modelo y ajustarlo de acuerdo con el resultado que queremos para obtener el resultado que queremos.
  11. Si le preocupa la producción dañina o tóxica 👉 Podríamos implementar una especie de «disyuntor» que ejecute la entrada del usuario para ver si hay discusiones tóxicas, dañinas o peligrosas. Por ejemplo, en un contexto de atención médica, podría ver si la información contenía lenguajes inseguros y responder en consecuencia, fuera del flujo típico.

El margen de mejora no se limita a estos puntos; las posibilidades son amplias y profundizaremos en ellas en futuros tutoriales. Hasta entonces, no dudes en contactar en twitter Si tienes alguna pregunta. Feliz FURIO :).

Esta publicación se publicó originalmente en learnbybuilding.ai. En los próximos meses impartiré un curso sobre cómo crear productos de IA generativa para gerentes de producto. Registrate aquí.