Herramientas de usuario

Herramientas del sitio


inteligencia_artificial:langchain:documentos

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anteriorRevisión previa
Próxima revisión
Revisión previa
inteligencia_artificial:langchain:documentos [2025/01/20 23:21] – [Troceado de documentos] albertointeligencia_artificial:langchain:documentos [2025/01/31 00:44] (actual) – [Base de datos vectorial] alberto
Línea 60: Línea 60:
  
 ===== Troceado de documentos ===== ===== Troceado de documentos =====
-La información almacenada debe "trocearse" para poder trabajar con ella sin problemas de tener ventanas de contexto demasiado grandes, que reduzcan el desempeño del LLM. \\+Los modelos de lenguaje, o LLM, sólo pueden examinar unos pocos miles de palabras de una vez, por lo que tenemos que necesitamos una solución para documentos que sean largos.  
 +{{ :ia:langchain_01_llm_inspeccionar.png?400 |}} 
 + 
 +Podemos "trocearla información almacenada para poder trabajar con ella sin problemas de tener ventanas de contexto demasiado grandes, que reduzcan el desempeño del LLM. \\
 Puede resultar sencillo conceptualmente, pero la forma de trocear la información tendrá un alto impacto en que el modelo sea capaz de encontrar la información deseada y realice su función adecuadamente. \\ Puede resultar sencillo conceptualmente, pero la forma de trocear la información tendrá un alto impacto en que el modelo sea capaz de encontrar la información deseada y realice su función adecuadamente. \\
  
Línea 70: Línea 73:
  
 A continuación algunos tipos de //text splitters// proporcionados por Langchain en **langchain.text_splitter.**: A continuación algunos tipos de //text splitters// proporcionados por Langchain en **langchain.text_splitter.**:
-  * **CharacterTextSplitter()**  +  * **CharacterTextSplitter()** Pensado para textos estructurados con separadores claros, como párrafos o líneas. Divide el texto en chunks basándose en un separador fijo (//separator// por defecto, \n). Intenta cumplir con un tamaño máximo de chunk (//chunk_size//) y solapamiento (//chunk_overlap//). Si no encuentra el separador, el texto puede no dividirse. 
-  * **MarkdownHeaderTextSplitter** +  * **MarkdownHeaderTextSplitter** Pensado para documentos en formato Markdown con encabezados jerárquicos (manuales técnicos, blogs, etc.). Divide los textos en función de los encabezados Markdown (como #, ##, ###, etc.), creando una estructura jerárquica de secciones y subsecciones. 
-  * **TokenTextSplitter()**  +  * **TokenTextSplitter()** Divide el texto en función del número de tokens. Los parámetros principales son //chunk_size// y //chunk_overlap//, refiriéndose al número de tokens. Pensado para manejar textos en función de limitaciones en el número de tokens. 
-  * **SentenceTransformersTokenTextSplitter()** +  * **SentenceTransformersTokenTextSplitter()** Similar a TokenTextSplitter, pero está diseñado para trabajar específicamente con modelos de Sentence Transformers, que funcionan mejor cuando se les alimenta con oraciones completas. Divide el texto en chunks asegurándose de que las oraciones no se corten arbitrariamente. 
-  * **RecursiveCharacterTextSplitter()** +  * **RecursiveCharacterTextSplitter()** Pensado para textos largos y desestructurados, donde es importante dividirlos de forma lógica (por párrafos, oraciones, palabras). Intenta dividir el texto de forma jerárquica. Usa una lista de separadores definidos (de más amplio a más específico) para dividir el texto. Por ejemplo: separators = ["\n\n", "\n", " ", ""]: Si no puede dividir el texto con un separador más amplio (como párrafos \n\n), intentará con uno más pequeño (como palabras ) y finalmente cortará arbitrariamente ("").  
-  * **Language** +  * **Language()** Usa el paquete langchain.text_splitter.Language para dividir texto basado en las características específicas de un idioma. Es una herramienta auxiliar, no un splitter completo. Se utiliza como base para otros splitters (por ejemplo, para configurar idiomas específicos en SpacyTextSplitter o NLTKTextSplitter). El parámetro principal es //language// para especificar el idioma del texto ("en", "es", etc). 
-  * **NLTKTextSplitter()** +  * **NLTKTextSplitter()** Pensado para textos donde es importante preservar oraciones completas, como artículos o transcripciones. Usa el módulo NLTK para dividir el texto en oraciones (tokenización basada en oraciones). Es ideal para textos donde el corte en oraciones es importante. Los parámetros principales son //language//, //chunk_size// y //chunk_overlap//. 
-  * **SpacyTextSplitter()**+  * **SpacyTextSplitter()** Pensado para textos donde la gramática y las oraciones completas son importantes, como documentos legales o textos científicos. Usa la biblioteca spaCy para dividir el texto basándose en la estructura lingüística. Puede identificar oraciones, párrafos o incluso dependencias gramaticales para realizar cortes naturales. Para usar el parámetro //language// para definir el idioma del texto, requiere que tenga instalado el modelo correspondiente, como //en_core_web_sm// para inglés.
  
 En el siguiente ejemplo vamos a usar 2 //text splitters// comunes, //recursive character text splitter// y //character text splitter//. En el siguiente ejemplo vamos a usar 2 //text splitters// comunes, //recursive character text splitter// y //character text splitter//.
-La principal diferencia entre ellos, es que en el caso de //character text splitter// sólo divide el texto si encuentra el carácter especificado, si no lo encuentra no respeta el //chunk_size//. En el caso de //recursive character text splitter//, sí respetará el //chunk_size// y cortará por defecto por cualquier carácter, aunque se puede definir los diferentes caracteres (por orden de prioridad) por los que "cortar", aproximándose lo máximo posible al //chunk_size//. En este último caso se pueden especificar expresiones regulares, con el parámetro //is_separator_regex=True//. \\+Para usar expresiones regulares con //recursive character text splitter// expresiones regulares, con el parámetro //is_separator_regex=True//. \\
  
 Lo vemos con unos ejemplos: Lo vemos con unos ejemplos:
Línea 178: Línea 181:
 ===== Salida ===== ===== Salida =====
 ===== Embeddings ===== ===== Embeddings =====
-Los modelos de lenguaje, o LLM, sólo pueden examinar unos pocos miles de palabras de una vez, por lo que tenemos que buscar una solución para documentos que sean largos. +Una vez "troceada" la información en partes significativashay que guardar éstas para poder trabajar con esta informaciónPara ello se recurre a //embeddings//.
-{{ :ia:langchain_01_llm_inspeccionar.png?400 |}} +
  
-Para almacenar la información de los documentos usamos //embeddings//Los embeddings crean representaciones numéricas de partes de texto. Estas representaciones, o vectores numéricos, capturan de alguna manera el significado de estas partes de texto, de forma que textos con contenidos similares, tendrán vectores similares. +Los embeddings generan representaciones numéricas de las partes de texto. Estas representaciones son vectores cuyo valor depende del significado de estas partes de texto, de forma que textos semánticamente similares, tendrán vectores similares. 
  
 {{ :ia:langchain_03_embeddings.png?500 |}} {{ :ia:langchain_03_embeddings.png?500 |}}
 {{ :ia:langchain_04_embeddings_ejemplo.png?400 |}} {{ :ia:langchain_04_embeddings_ejemplo.png?400 |}}
  
-En el ejemplo de la imagen, los dos primeros casos hablan de mascotas, mientras que el tercero habla de un coche, por lo que los vectores entre los dos primeros son similares (cerca espacialmente, dado que son vectores), mientras que el tercero es muy diferente. Ya intuimos que esto se usa para realizar búsquedas de información.\\+En el ejemplo de la imagen, los dos primeros casos hablan de mascotas, mientras que el tercero habla de un coche, por lo que los vectores entre los dos primeros son similares (cerca espacialmente, dado que son vectores), mientras que el tercero es muy diferente. \\
  
- +Los embeddings dependen de un modelo de IA. OpenAI tiene sus embeddings, Llama los suyos, etc. De modo que podemos usar sus APIs para generar estos vectores, aunque no se usen sus LLM. \\  
-===== Base de datos de vectores ===== +===== Base de datos vectorial ===== 
-A la base de datos de vectores le pasamos los vectores resultantes de aplicar los embeddings al texto de nuestros documentos. \\ +Los vectores generados a través de embeddings los guardamos en una base de datos vectorial, desde la que trabajaremos para hacer búsquedas semánticas y recuperar información. \\
- +
-Si los documentos son muy grandes, hay que "trocearlos" en partes más pequeñas para ayudar al modelo a procesar la información (tienen una capacidad limitada de cantidad de //tokens// a procesar de una vez). Se pasarán al modelo los "trozos" de información más relevantes para la consulta realizada. Estos trozos de información, son habitualmente denominados "documentos".+
  
 {{ :ia:langchain_05_vector_database.png?500 |}} {{ :ia:langchain_05_vector_database.png?500 |}}
Línea 201: Línea 200:
 {{ :ia:langchain_06_vector_database_index.png?500 |}} {{ :ia:langchain_06_vector_database_index.png?500 |}}
  
-La consulta se pasa a un vector a través de un embedding, y se compara con los diferentes vectores de cada porción de información. Se devuelven al modelo los más parecidos para poder devolver la respuesta definitiva.+Para realizar una búsqueda semántica en la base de datos, se pasa la consulta a vector a través de embeddings, y se compara con los diferentes vectores de cada porción de información. Se devuelven al modelo los más parecidos para poder devolver la respuesta definitiva.
  
 {{ :ia:langchain_08_vector_database.png?400 |}} {{ :ia:langchain_08_vector_database.png?400 |}}
  
 +En langchain existen más de 30 tipos de base de datos vectoriales diferentes que pueden [[https://python.langchain.com/docs/integrations/vectorstores/|consultarse en su web]].
 +
 +En el siguiente ejemplo cargamos el texto de varios PDFs, dividimos la información en partes, usando los embeddings de Llama 3.1 pasamos las partes a vectores, y finalmente los guardamos en una base de datos vectorial de tipo Chroma por su simplicidad:
 +<code python>
 +from langchain_community.document_loaders import PyPDFLoader
 +
 +# Cargamos los documentos
 +loaders = [
 +    PyPDFLoader("seres_01_Hada.pdf"),
 +    PyPDFLoader("seres_01_Hada.pdf"),   # Repetimos el mismo documento para ver como se comporta el sistema
 +    PyPDFLoader("seres_02_Gnomo.pdf"),
 +    PyPDFLoader("seres_03_Trasgo.pdf"),
 +    PyPDFLoader("seres_04_Mago.pdf"),
 +]
 +
 +docs = []
 +for loader in loaders:
 +    docs.extend(loader.load())
 +
 +# Troceamos los documentos
 +from langchain.text_splitter import RecursiveCharacterTextSplitter
 +text_splitter = RecursiveCharacterTextSplitter(
 +    chunk_size = 500,
 +    chunk_overlap = 50
 +)
 +
 +splits = text_splitter.split_documents(docs)
 +
 +# Definimos el modelo de embeddings
 +from langchain_ollama import OllamaEmbeddings
 +
 +embedding = OllamaEmbeddings(model="llama3.1")
 +
 +# Guardamos los datos en una base de datos vectorial
 +from langchain_community.vectorstores import Chroma
 +
 +# Definimos el directorio donde se guardará la base de datos vectorial
 +persist_directory = 'docs/chroma'
 +
 +# Eliminamos el directorio si ya existe, es decir, borramos la base de datos anterior antes de volver a lanzar el sript
 +import shutil
 +
 +path = "docs/chroma"
 +
 +try:
 +    shutil.rmtree(path)
 +    print(f"Directorio {path} eliminado")
 +
 +except FileNotFoundError:
 +    print(f"Directorio {path} no encontrado")
 +
 +except Exception as e:
 +    print(f"Error al eliminar el directorio: {e}")
 +
 +# Creamos la base de datos vectorial
 +vectordb = Chroma.from_documents(
 +    documents = splits,
 +    embedding = embedding,
 +    persist_directory = persist_directory
 +)
 +
 +# Vamos a realizar una consulta
 +question = "¿En qué parte de la península hay seres fantásticos como hadas y gnomos?"
 +docus = vectordb.similarity_search(question, k=3) # En langchain el texto devuelto de una base de datos vectorial al realizar una búsqueda semántica es de tipo "document", por eso se le suele llamar "docs" o documentos.
 +print(f"Número de documentos encontrados: {len(docus)}")
 +
 +print(f"\nPrimer resultado: {docus[0].page_content}")
 +print(f"\nSegundo resultado: {docus[1].page_content}")
 +print(f"\nTercer resultado: {docus[2].page_content}")
 +</code>
 +
 +Al final del ejemplo anterior realizamos una búsqueda en la base de datos.
 +Al haber documentos duplicados, podemos obtener diferentes respuestas prácticamente iguales.
 +También puede ocurrir que se obtenga la información de algunos documentos (vectores), cuando hay otros más idóneos para la consulta realizada. 
 +Veremos en el siguiente apartado //retrieval// como obtener información de la base de datos vectorial adecuadamente.
 ===== Métodos de extracción de información ===== ===== Métodos de extracción de información =====
 Hay varios métodos a partir de los cuales se puede extraer la información más importante (la más similar vectorialmente) de las diferentes partes en que se dividió la información. Hay varios métodos a partir de los cuales se puede extraer la información más importante (la más similar vectorialmente) de las diferentes partes en que se dividió la información.
inteligencia_artificial/langchain/documentos.1737411688.txt.gz · Última modificación: por alberto

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki