Integrar Milvus con DSPy
Qué es DSPy
DSPy, introducido por el Stanford NLP Group, es un marco programático innovador diseñado para optimizar las indicaciones y los pesos dentro de los modelos lingüísticos, especialmente valioso en escenarios en los que se integran grandes modelos lingüísticos (LLM) en múltiples etapas de un proceso. A diferencia de las técnicas convencionales de ingeniería de avisos, que dependen de la elaboración y los ajustes manuales, DSPy adopta un enfoque basado en el aprendizaje. Mediante la asimilación de ejemplos de preguntas-respuestas, DSPy genera mensajes optimizados de forma dinámica, adaptados a tareas específicas. Esta metodología innovadora permite reensamblar sin problemas cadenas enteras, eliminando la necesidad de realizar continuos ajustes manuales de las instrucciones. La sintaxis pitónica de DSPy ofrece varios módulos componibles y declarativos, simplificando la instrucción de los LLM.
Ventajas del uso de DSPy
- Enfoque de programación: DSPy proporciona un enfoque de programación sistemático para el desarrollo de canalizaciones LM mediante la abstracción de canalizaciones como gráficos de transformación de texto en lugar de limitarse a indicar los LLM. Sus módulos declarativos permiten el diseño estructurado y la optimización, sustituyendo el método de ensayo y error de las plantillas tradicionales.
- Mejora del rendimiento: DSPy demuestra una mejora significativa del rendimiento con respecto a los métodos existentes. A través de estudios de casos, supera a los avisos estándar y a las demostraciones creadas por expertos, mostrando su versatilidad y eficacia incluso cuando se compila en modelos LM más pequeños.
- Abstracción modularizada: DSPy abstrae eficazmente los aspectos intrincados del desarrollo de canalizaciones LM, como la descomposición, el ajuste fino y la selección de modelos. Con DSPy, un programa conciso puede traducirse perfectamente en instrucciones para varios modelos, como GPT-4, Llama2-13b o T5-base, agilizando el desarrollo y mejorando el rendimiento.
Módulos
Existen numerosos componentes que contribuyen a construir un canal LLM. A continuación, describiremos algunos componentes clave para proporcionar una comprensión de alto nivel del funcionamiento de DSPy.
Módulos DSPy
Firma: Las firmas en DSPy sirven como especificaciones declarativas, delineando el comportamiento de entrada/salida de los módulos, guiando el modelo de lenguaje en la ejecución de tareas. Módulo: Los módulos DSPy sirven como componentes fundamentales para los programas que aprovechan los modelos de lenguaje (LM). Abstraen diversas técnicas de instrucción, como la cadena de pensamiento o ReAct, y son adaptables para manejar cualquier firma DSPy. Con parámetros aprendibles y la capacidad de procesar entradas y producir salidas, estos módulos pueden combinarse para formar programas más amplios, inspirándose en los módulos NN de PyTorch pero adaptados a las aplicaciones LM. Optimizador: Los optimizadores en DSPy ajustan los parámetros de los programas DSPy, como las indicaciones y los pesos LLM, para maximizar las métricas especificadas como la precisión, mejorando la eficiencia del programa.
Por qué Milvus en DSPy
DSPy es un potente marco de programación que potencia las aplicaciones RAG. Estas aplicaciones necesitan recuperar información útil para mejorar la calidad de las respuestas, lo que requiere una base de datos vectorial. Milvus es una conocida base de datos vectorial de código abierto para mejorar el rendimiento y la escalabilidad. Con MilvusRM, un módulo recuperador en DSPy, la integración de Milvus se realiza sin problemas. Ahora, los desarrolladores pueden definir y optimizar fácilmente programas RAG utilizando DSPy, aprovechando las potentes capacidades de búsqueda vectorial de Milvus. Esta colaboración hace que las aplicaciones RAG sean más eficientes y escalables, combinando las capacidades de programación de DSPy con las funciones de búsqueda de Milvus.
Ejemplos
Ahora, vamos a recorrer un ejemplo rápido para demostrar cómo aprovechar Milvus en DSPy para optimizar una aplicación RAG.
Requisitos previos
Antes de construir la aplicación RAG, instale DSPy y PyMilvus.
$ pip install "dspy-ai[milvus]"
$ pip install -U pymilvus
Cargar el conjunto de datos
En este ejemplo, utilizamos HotPotQA, una colección de pares de preguntas-respuestas complejas, como nuestro conjunto de datos de entrenamiento. Podemos cargarlos a través de la clase HotPotQA.
from dspy.datasets import HotPotQA
# Load the dataset.
dataset = HotPotQA(
train_seed=1, train_size=20, eval_seed=2023, dev_size=50, test_size=0
)
# Tell DSPy that the 'question' field is the input. Any other fields are labels and/or metadata.
trainset = [x.with_inputs("question") for x in dataset.train]
devset = [x.with_inputs("question") for x in dataset.dev]
Ingesta de datos en la base de datos vectorial Milvus
Introduzca la información contextual en la colección Milvus para la recuperación de vectores. Esta colección debe tener un campo embedding
y un campo text
. En este caso utilizamos el modelo text-embedding-3-small
de OpenAI como función de incrustación de consultas por defecto.
import requests
import os
os.environ["OPENAI_API_KEY"] = "<YOUR_OPENAI_API_KEY>"
MILVUS_URI = "example.db"
MILVUS_TOKEN = ""
from pymilvus import MilvusClient, DataType, Collection
from dspy.retrieve.milvus_rm import openai_embedding_function
client = MilvusClient(uri=MILVUS_URI, token=MILVUS_TOKEN)
if "dspy_example" not in client.list_collections():
client.create_collection(
collection_name="dspy_example",
overwrite=True,
dimension=1536,
primary_field_name="id",
vector_field_name="embedding",
id_type="int",
metric_type="IP",
max_length=65535,
enable_dynamic=True,
)
text = requests.get(
"https://raw.githubusercontent.com/wxywb/dspy_dataset_sample/master/sample_data.txt"
).text
for idx, passage in enumerate(text.split("\n")):
if len(passage) == 0:
continue
client.insert(
collection_name="dspy_example",
data=[
{
"id": idx,
"embedding": openai_embedding_function(passage)[0],
"text": passage,
}
],
)
Definir MilvusRM.
Ahora tiene que definir el MilvusRM.
from dspy.retrieve.milvus_rm import MilvusRM
import dspy
retriever_model = MilvusRM(
collection_name="dspy_example",
uri=MILVUS_URI,
token=MILVUS_TOKEN, # ignore this if no token is required for Milvus connection
embedding_function=openai_embedding_function,
)
turbo = dspy.OpenAI(model="gpt-3.5-turbo")
dspy.settings.configure(lm=turbo)
Creación de firmas
Ahora que hemos cargado los datos, empecemos a definir las firmas para las subtareas de nuestro pipeline. Podemos identificar nuestra simple entrada question
y salida answer
, pero dado que estamos construyendo un pipeline RAG, recuperaremos información contextual de Milvus. Así que vamos a definir nuestra firma como context, question --> answer
.
class GenerateAnswer(dspy.Signature):
"""Answer questions with short factoid answers."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")
Incluimos breves descripciones de los campos context
y answer
para definir directrices más claras sobre lo que el modelo recibirá y deberá generar.
Construcción de la canalización
Ahora vamos a definir el proceso RAG.
class RAG(dspy.Module):
def __init__(self, rm):
super().__init__()
self.retrieve = rm
# This signature indicates the task imposed on the COT module.
self.generate_answer = dspy.ChainOfThought(GenerateAnswer)
def forward(self, question):
# Use milvus_rm to retrieve context for the question.
context = self.retrieve(question).passages
# COT module takes "context, query" and output "answer".
prediction = self.generate_answer(context=context, question=question)
return dspy.Prediction(
context=[item.long_text for item in context], answer=prediction.answer
)
Ejecutar el proceso y obtener los resultados
Ahora, hemos construido esta canalización RAG. Probémosla y obtengamos resultados.
rag = RAG(retriever_model)
print(rag("who write At My Window").answer)
Townes Van Zandt
Podemos evaluar los resultados cuantitativos en el conjunto de datos.
from dspy.evaluate.evaluate import Evaluate
from dspy.datasets import HotPotQA
evaluate_on_hotpotqa = Evaluate(
devset=devset, num_threads=1, display_progress=False, display_table=5
)
metric = dspy.evaluate.answer_exact_match
score = evaluate_on_hotpotqa(rag, metric=metric)
print("rag:", score)
Optimización del programa
Después de definir este programa, el siguiente paso es la compilación. Este proceso actualiza los parámetros dentro de cada módulo para mejorar el rendimiento. El proceso de compilación depende de tres factores críticos:
- Conjunto de entrenamiento: Utilizaremos los 20 ejemplos de pregunta-respuesta de nuestro conjunto de datos de entrenamiento para esta demostración.
- Métrica de validación: Estableceremos una métrica sencilla
validate_context_and_answer
. Esta métrica verifica la precisión de la respuesta predicha y garantiza que el contexto recuperado incluye la respuesta. - Optimizador específico (Teleprompter): El compilador de DSPy incorpora múltiples teleprompters diseñados para optimizar sus programas de forma efectiva.
from dspy.teleprompt import BootstrapFewShot
# Validation logic: check that the predicted answer is correct.# Also check that the retrieved context does contain that answer.
def validate_context_and_answer(example, pred, trace=None):
answer_EM = dspy.evaluate.answer_exact_match(example, pred)
answer_PM = dspy.evaluate.answer_passage_match(example, pred)
return answer_EM and answer_PM
# Set up a basic teleprompter, which will compile our RAG program.
teleprompter = BootstrapFewShot(metric=validate_context_and_answer)
# Compile!
compiled_rag = teleprompter.compile(rag, trainset=trainset)
# Now compiled_rag is optimized and ready to answer your new question!
# Now, let’s evaluate the compiled RAG program.
score = evaluate_on_hotpotqa(compiled_rag, metric=metric)
print(score)
print("compile_rag:", score)
La puntuación de Ragas ha aumentado de su valor anterior de 50,0 a 52,0, lo que indica una mejora en la calidad de la respuesta.
Resumen
DSPy marca un salto en las interacciones de los modelos lingüísticos gracias a su interfaz programable, que facilita la optimización algorítmica y automatizada de los teleprompters y pesos de los modelos. Al aprovechar DSPy para la implementación de la GAR, la adaptabilidad a diferentes modelos lingüísticos o conjuntos de datos se convierte en un juego de niños, reduciendo drásticamente la necesidad de tediosas intervenciones manuales.