新しい投稿

検索

記事
· 2024年10月14日 6m read

LLM Models and RAG Applications Step-by-Step - Part II - Creating the Context

We continue with this series of articles on LLM and RAG applications and in this article we will discuss the red boxed part of the following diagram:

In the process of creating a RAG application, choosing an LLM model that is appropriate to your needs (trained in the corresponding subject, costs, speed, etc.) is as important as having a clear understanding of the context you want to provide. Let's start by defining the term to be clear about what we mean by context.

What is context?

Context refers to additional information obtained from an external source, such as a database or search engine, to supplement or enhance the responses generated by a language model. The language model uses this relevant external information to generate more accurate and detailed responses rather than relying solely on what it has learned during its training. Context helps keep responses up-to-date and aligned with the specific topic of the query.

This context can be information stored in a database with tools similar to those shown by our dear community member @José Pereira in this article or unstructured information in the form of text files with which we will feed the LLM, which will be the case we are going to deal with here.

How to generate the context for our RAG application?

The first and most essential thing is, obviously, to have all the information that we consider may be relevant for the possible queries that are going to be made against our application. Once this information is arranged in such a way that it is accessible from our application, we must be able to identify which of all the documents available for our context refer to the specific question asked by the user. For our example, we have a series of PDF documents (medication leaflets) that we want to use as a possible context for the questions of the users of our application.

This point is key to the success of a RAG application, as it is just as bad for a user's confidence to answer with generalizations and vagueness typical of an LLM as it is to answer with a totally wrong context. This is where our beloved vector databases come in.

Vector databases

You have probably heard of "vector databases" before, as if they were a new type of database, such as relational or document databases. Nothing could be further from the truth. These vector databases are standard databases that support vector data types as well as operations related to them. Let's see how this type of data will be represented in the project associated with the article:

Now let's take a look at how a record would be displayed:

Vector databases...what for?

As we explained in the previous article with LLMs, the use of vectors is key in language models, as they can represent concepts and the relationships between them in a multidimensional space. In the case at hand, this multidimensional representation will be the key to identifying which of the documents in our context will be relevant to the question asked.

Perfect, we have our vector database and the documents that will provide the context, now we just need to record the content of these documents within our database, but... With what criteria?

Models for vectorization

How? Another model? Isn't the LLM enough for us? Well... there's no need to bother our LLM to vectorize the information of our context, we can use smaller language models that are more suited to our needs for this task, such as models trained to detect similarities between sentences. You can find a myriad of them in Hugging Face,  each one trained with a specific set of data that will allow us to improve the vectorization of our data.

And if this does not convince you to use one of these models for vectorization, just say that generally this type of models...

Let's see in our example how we invoke the chosen model for these vectorizations:

if not os.path.isdir('/app/data/model/'):
    model = sentence_transformers.SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')            
    model.save('/app/data/model/')

Here we are downloading the model chosen for our case to our local computer. This mini-LM is multilingual so we can vectorize in both Spanish and English without any problem.

Chunking

If you have already tinkered with language models, you have probably already faced the challenge of chunking. What is this chunking? Very simple, it is the division of the text into smaller fragments that may contain a relevant meaning. By means of this chunking of our context, we can make queries on our vector database that extract those documents from our context that may be relevant in relation to the question asked.

What are the criteria for this chunking? Well, there really isn't a magic criterion that allows us to know how long our chunks have to be in order to be as accurate as possible. In our example we are using a Python library provided by langchain to perform this chunking, although any other method or library could be used for this:

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 700,
    chunk_overlap  = 50,
)
path = "/app/data"
loader = PyPDFDirectoryLoader(path)
docs_before_split = loader.load()
docs_after_split = text_splitter.split_documents(docs_before_split)

As you can see, the chosen size is 700 characters, with an overlap of 50 to avoid cutting words. These fragments extracted from our documents will be the ones we will vectorize and insert into our database.

This chunking process can be optimized as much as you want by means of "lemmatization", through which we can transform the words into their corresponding lemma (without tenses, plurals, gender, etc.) and thus eliminate certain noise for the generation of the vector, but we are not going to go into that, on this page you can see a more detailed explanation.

Vectorization of fragments

Ok, we have our snippets extracted from each of our documents, it's time to vectorize and insert into our database, let's take a look at the code to understand how we could do it.

for doc in docs_after_split:
    embeddings = model.encode(doc.page_content, normalize_embeddings=True)
    array = np.array(embeddings)
    formatted_array = np.vectorize('{:.12f}'.format)(array)
    parameters = []
    parameters.append(doc.metadata['source'])
    parameters.append(str(doc.page_content))
    parameters.append(str(','.join(formatted_array)))
    cursorIRIS.execute("INSERT INTO LLMRAG.DOCUMENTCHUNK (Document, Phrase, VectorizedPhrase) VALUES (?, ?, TO_VECTOR(?,DECIMAL))", parameters)
connectionIRIS.commit()

As you can see, we will carry out the following steps: 

  1. We go through the list of all the pieces obtained from all the documents that will form our context.
  2. For each fragment we vectorize the text (using the sentence_transformers library). 
  3. We create an array using the numpy library with the formatted vector and transform it into a string.
  4. We register the document information with its associated vector in our database. If you see, we are executing the TO_VECTOR command that will transform the vector string that we have passed to the appropriate format.

Conclusion

In this article we have seen the need to have a vector database for the creation of the necessary context in our RAG application, we have also reviewed how to cut up and vectorize the information of our context for its registration in said database.

In the next article we will see how to query our vector database based on the question that the user sends to the LLM model and how, by searching for similarities, we will build the context that we will pass to the model. Don't miss it!

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
記事
· 2024年10月14日 6m read

Modelos LLM y aplicaciones RAG paso a paso - Parte II - Creando el contexto

Continuamos con esta serie de artículos sobre LLM y aplicaciones RAG y en este artículo trataremos la parte recuadrada en rojo del siguiente diagrama:

En el proceso de creación de una aplicación RAG tan importante es la elección de un modelo de LLM adecuado a tus necesidades (entrenado en la temática correspondiente, costes del mismo, velocidad, etc) como el tener claro el contexto que queremos proporcionarle. Empecemos definiendo el término para tener claro a que nos referimos con contexto.

¿Qué es el contexto?

El contexto se refiere a la información adicional que se obtiene de una fuente externa, como una base de datos o un sistema de búsqueda, para complementar o mejorar las respuestas generadas por un modelo de lenguaje. El modelo de lenguaje utiliza esta información externa relevante para generar respuestas más precisas y detalladas en lugar de basarse únicamente en lo que ha aprendido durante su entrenamiento. El contexto ayuda a que las respuestas estén actualizadas y alineadas con el tema específico de la consulta.

Este contexto puede ser desde información almacenada en una base de datos con herramienta similares a la mostrada por nuestro querido miembro de la comunidad @José Pereira en este artículo o información no estructurada en forma de archivos de texto con los que alimentaremos el LLM, que será el caso que vamos a tratar aquí.

¿Cómo generar el contexto para nuestra aplicación RAG?

Lo primero y más indispensable es, obviamente, contar con toda la información que consideramos que puede ser relevante para las posibles consultas que se van a realizar contra nuestra aplicación. Una vez dispuesta dicha información de tal forma que sea accesible desde nuestra aplicación deberemos poder identificar cuales de todos los documentos disponibles para nuestro contexto se refieren a la pregunta específica realizada por el usuario. Para nuestro ejemplo contamos con una serie de documentos en PDF (prospectos de medicamentos) que vamos a querer utilizar como posible contexto ante las preguntas de los usuarios de nuestra aplicación.

Este punto es clave para el éxito de una aplicación RAG, tan malo es para la confianza de un usuario contestarle con generalizaciones y vaguedades típicas de un LLM como contestarle con un contexto totalmente equivocado. Es aquí donde entran nuestras queridas bases de datos vectoriales.

Bases de datos vectoriales

Seguramente ya habéis oído hablar antes de las "bases de datos vectoriales" como si estas fueran un nuevo tipo de base de datos como pueden ser las bases de datos relacionales o las documentales, nada más lejos de la realidad, estas bases de datos vectoriales son bases de datos al uso que soportan los tipos de datos vectoriales así como las operaciones relacionadas con los mismos. Veamos en el proyecto asociado al artículo como se representará ese tipo de datos:

Ahora echemos un vistazo a cómo se visualizaría un registro:

Bases de datos vectoriales...¿Para qué?

Como explicamos en el artículo anterior con los LLM la utilización de vectores es clave en los modelos de lenguaje, al poder representar en un espacio multidimensional los conceptos y las relaciones entre los mismos. Para el caso que nos atañe esta representación multidimensional será la clave para identificar cuales de los documentos de nuestro contexto serán relevantes para la pregunta realizada.

Perfecto, tenemos nuestra base de datos vectorial y los documentos que aportarán el contexto, ahora sólo necesitamos registrar el contenido de estos documentos dentro de nuestra base de datos, pero... ¿Con qué criterio?

Modelos para el vectorizado

¿Cómo? ¿Otro modelo? ¿No nos vale con el LLM? Pues...no es necesario molestar a nuestro LLM para que nos vectorice la información de nuestro contexto, podremos utilizar modelos más pequeños de lenguaje que se adecúen más a nuestras necesidades para esta tarea, como pueden ser los modelos entrenados para detectar similitudes entre frases. Podéis encontrar una miriada de los mismos en Hugging Face cada uno entrenado con un determinado conjunto de datos que nos permitirá mejorar la vectorización de nuestros datos.

Y si esto no os convence para utilizar uno de estos modelos para la vectorización sólo decir que por lo general este tipo de modelos...

Veamos en nuestro ejemplo como invocamos al modelo elegido para estas vectorizaciones:

if not os.path.isdir('/app/data/model/'):
    model = sentence_transformers.SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')            
    model.save('/app/data/model/')

Aquí estamos descargando en nuestro equipo local el modelo elegido para nuestro caso, este mini-LM es multiidioma por lo que podremos vectorizar tanto en español como en inglés sin problema.

Trozeado o "chunking"

Si ya habéis trasteado con modelos de lenguaje seguramente ya os hayáis enfrentado al reto del chunking. ¿Qué es este chunking? Muy sencillo, es la división del texto en fragmentos menores que puedan contener un significado relevante. Mediante este troceado de nuestro contexto podremos realizar consultas sobre nuestra base de datos vectorial que nos extraiga aquellos documentos de nuestro contexto que puedan ser relevantes en relación a la pregunta realizada.

¿Cuales son los criterios para este troceado? Pues realmente no hay un criterio mágico que nos permita saber cómo de largos tienen que ser nuestros trozos para que sean lo más precisos posibles. En nuestro ejemplo estamos utilizando una librería de Python proporcionada por langchain para realizar este troceado, aunque se podría utilizar cualquier otro método o librería para ello:

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 700,
    chunk_overlap  = 50,
)
path = "/app/data"
loader = PyPDFDirectoryLoader(path)
docs_before_split = loader.load()
docs_after_split = text_splitter.split_documents(docs_before_split)

Como véis el tamaño elegido es de 700 caracteres, con un solape de 50 para evitar cortar palabras. Estos fragmentos extraidos de nuestros documentos serán los que vectorizaremos e insertaremos en nuestra base de datos.

Este proceso de troceado se puede optimizar tanto como se quiera mediante la "lematización", mediante el cual podemos transformar las palabras en su lema correspondiente (sin tiempos verbales, plurales, género, etc) y de tal forma eliminar cierto ruido de cara a la generación del vector, pero no vamos a entrar en ello, en esta página podéis ver una explicación más detallada.

Vectorización de fragmentos

Muy bien, tenemos nuestros fragmentos extraídos de cada uno de nuestros documentos, es el momento de vectorizar e insertar en nuestra base de datos, echemos un vistazo al código para entender como lo podríamos hacer.

for doc in docs_after_split:
    embeddings = model.encode(doc.page_content, normalize_embeddings=True)
    array = np.array(embeddings)
    formatted_array = np.vectorize('{:.12f}'.format)(array)
    parameters = []
    parameters.append(doc.metadata['source'])
    parameters.append(str(doc.page_content))
    parameters.append(str(','.join(formatted_array)))
    cursorIRIS.execute("INSERT INTO LLMRAG.DOCUMENTCHUNK (Document, Phrase, VectorizedPhrase) VALUES (?, ?, TO_VECTOR(?,DECIMAL))", parameters)
connectionIRIS.commit()

Como véis realizaremos los siguientes pasos: 

  1. Recorremos la lista de todos los trozos obtenidos de todos los documentos que van a formar nuestro contexto.
  2. Para cada fragmento vectorizamos el texto (utilizando la librería sentence_transformers). 
  3. Creamos un array utilizando la librería de numpy con el vector formateado y lo transformamos en un string.
  4. Registramos la información del documento con su vector asociado en nuestra base de datos. Si veis estamos ejecutando el comando TO_VECTOR que nos transformará el string del vector que le hemos pasado al formato adecuado.

Conclusión

En este artículo hemos podido ver la necesidad de disponer de una base de datos vectorial para la creación del contexto necesario en nuestra aplicación RAG, también hemos repasado como trocear y vectorizar la información de nuestro contexto para su registro en dicha base de datos.

En el próximo artículo veremos como consultar nuestra base de datos vectorial a partir de la pregunta que el usuario envíe al modelo de LLM y como, mediante la búsqueda de similitudes, montaremos el contexto que pasaremos al modelo. ¡No te lo pierdas!

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
ダイジェスト
· 2024年10月14日

Publications des développeurs d'InterSystems, semaine Octobre 07 - 13, 2024, Résumé

Articles
Annonces
Octobre 07 - 13, 2024Week at a GlanceInterSystems Developer Community
ダイジェスト
· 2024年10月14日
質問
· 2024年10月14日

How does VSCode work with multiple IRIS instance on the same server

Hi All,

I'm trying to connect to an IRIS instance using VSCode, the problem is, there are 2 IRIS instances installed on this server, published by the same hostname using https (port 443) under 2 different subfolders (e.g., iris.demo.com/base & iris.demo.com/test).

When connecting via studio, I was able to connect by using the hostname (e.g., iris.demo.com) and specifying the super server port, while when connecting through VSCode, when specifying the web server port 443, the connection is always defaulted to one of the instance.

Is there any way we can specify the instance in VSCode?

Many thanks!

2 Comments
ディスカッション (2)2
続けるにはログインするか新規登録を行ってください