¡Namasté! Soy de la India, donde hay cuatro estaciones: invierno, verano, monzón y otoño. ¿Puedes adivinar qué temporada odio más? Es temporada de impuestos.
Este año, como de costumbre, me apresuré a examinar varias secciones y documentos del impuesto sobre la renta para maximizar mis ahorros (legalmente, por supuesto, ). Miré innumerables videos y revisé documentos, algunos en inglés, otros en hindi, con la esperanza de encontrar las respuestas que necesitaba. Pero, cuando sólo me quedaban dos días para presentar la declaración de impuestos, me di cuenta de que no tenía tiempo para repasarlo todo. En ese momento, deseaba que hubiera una forma rápida de obtener respuestas, ¡sin importar el idioma!
Aunque RAG (Retrieval Augmented Generation) podía hacer esto, la mayoría de los tutoriales y modelos solo se centraban en documentos en inglés, dejando los que no estaban en inglés en gran medida sin soporte. Fue entonces cuando me di cuenta: podía crear un canal RAG diseñado para contenido indio, un sistema RAG que podría responder preguntas hojeando documentos hindi. ¡Y así empezó el viaje!
Cuaderno: si eres más una persona que usa cuadernos, también he subido el código completo a un cuaderno de Colab. Puede compruébalo aquí. Recomiendo ejecutarlo en un entorno de GPU T4 en Colab.
Entonces comencemos. Tudum!
Resultados de aprendizaje
- Comprenda cómo crear una canalización de generación aumentada de recuperación (RAG) de un extremo a otro para procesar documentos en hindi.
- Aprenda técnicas para rastrear, limpiar y estructurar datos de texto en hindi para aplicaciones de PNL.
- Aprenda cómo aprovechar los LLM índicos para crear canales RAG para documentos en idioma indio, mejorando el procesamiento de documentos multilingües.
- Explore el uso de modelos de código abierto como E5 multilingüe y Airavata para incrustaciones y generación de texto en hindi.
- Configure y administre Chroma DB para un almacenamiento y recuperación de vectores eficiente en sistemas RAG.
- Obtenga experiencia práctica con la ingesta, recuperación y respuesta de preguntas de documentos utilizando un canal RAG en idioma hindi.
Este artículo fue publicado como parte del Blogatón de ciencia de datos.
Recopilación de datos: obtención de información fiscal en hindi
El viaje comenzó con la recopilación de datos, comencé con algunos artículos de noticias y sitios web relacionados con información sobre impuestos sobre la renta en la India, escritos en hindi. Incluye preguntas frecuentes y texto no estructurado que cubre secciones de deducción de impuestos, preguntas frecuentes y formularios requeridos. Puedes consultarlos aquí:
urls =('https://www.incometax.gov.in/iec/foportal/hi/help/e-filing-itr1-form-sahaj-faq',
'https://www.incometax.gov.in/iec/foportal/hi/help/e-filing-itr4-form-sugam-faq',
'https://navbharattimes.indiatimes.com/business/budget/budget-classroom/income-tax-sections-know-which-section-can-save-how-much-tax-here-is-all-about-income-tax-law-to-understand-budget-speech/articleshow/89141099.cms',
'https://www.incometax.gov.in/iec/foportal/hi/help/individual/return-applicable-1',
'https://www.zeebiz.com/hindi/personal-finance/income-tax/tax-deductions-under-section-80g-income-tax-exemption-limit-how-to-save-tax-on-donation-money-to-charitable-trusts-126529'
)
Limpieza y análisis de datos
La preparación de los datos implica los siguientes pasos:
- Rastreando los datos de las páginas web
- Limpiar los datos
Veamos cada uno de ellos uno por uno.
Arrastrándose
Usaré una de mis bibliotecas favoritas para rastrear sitios web.Rastreador de rebajas. Puede instalarlo usando el comando que se menciona a continuación. Analiza el sitio web en formato Markdown y lo almacena en archivos Markdown.
!pip install markdown-crawler
!pip install markdownify
Una característica interesante de Markdown Crawler es su capacidad no sólo de rastrear las páginas web principales sino también de explorar las páginas vinculadas dentro del sitio, gracias a sus parámetros de profundidad. Esto permite un rastreo de sitios web más completo. Pero en nuestro caso no necesitamos eso, por lo que la profundidad será cero.
Aquí está la función para rastrear URL
from markdown_crawler import md_crawl
def crawl_urls(urls: list, storage_folder_path: str, max_depth=0):
# Iterate over each URL in the list
for url in urls:
print(f"Crawling {url}") # Output the URL being crawled
# Crawl the URL and save the result in the specified folder
md_crawl(url, max_depth=max_depth, base_dir=storage_folder_path, is_links=True)
urls =('https://www.incometax.gov.in/iec/foportal/hi/help/e-filing-itr1-form-sahaj-faq',
'https://www.incometax.gov.in/iec/foportal/hi/help/e-filing-itr4-form-sugam-faq',
'https://navbharattimes.indiatimes.com/business/budget/budget-classroom/income-tax-sections-know-which-section-can-save-how-much-tax-here-is-all-about-income-tax-law-to-understand-budget-speech/articleshow/89141099.cms',
'https://www.incometax.gov.in/iec/foportal/hi/help/individual/return-applicable-1',
'https://www.zeebiz.com/hindi/personal-finance/income-tax/tax-deductions-under-section-80g-income-tax-exemption-limit-how-to-save-tax-on-donation-money-to-charitable-trusts-126529'
)
crawl_urls(urls= urls, storage_folder_path="./incometax_documents/")
#you do not need to make a folder intitially. Md Crawler handles that for you.\#import csv
Este código guardará los archivos Markdown analizados en la carpeta Incometax_documents.
Limpiar los datos
A continuación, necesitamos crear un analizador que lea los archivos Markdown y los divida en secciones. Si está trabajando con datos diferentes que ya están procesados, puede omitir este paso.
Primero, escribamos funciones para extraer contenido de un archivo. Usaremos la reducción de bibliotecas de Python y BeautifulSoup para esto. A continuación se muestran los comandos para instalar estas bibliotecas:
!pip install beautifulsoup4
!pip install markdown#import csv
import markdown
from bs4 import BeautifulSoup
def read_markdown_file(file_path):
"""Read a Markdown file and extract its sections as headers and content."""
# Open the markdown file and read its content
with open(file_path, 'r', encoding='utf-8') as file:
md_content = file.read()
# Convert markdown to HTML
html_content = markdown.markdown(md_content)
# Parse HTML content
soup = BeautifulSoup(html_content, 'html.parser')
sections = ()
current_section = None
# Loop through HTML tags
for tag in soup:
# Start a new section if a header tag is found
if tag.name and tag.name.startswith('h'):
if current_section:
sections.append(current_section)
current_section = {'header': tag.text, 'content': ''}
# Add content to the current section
elif current_section:
current_section('content') += tag.get_text() + '\n'
# Add the last section
if current_section:
sections.append(current_section)
return sections
#lets look at the output of one of the files:
sections = read_markdown_file('./incometax_documents/business-budget-budget-classroom-income-tax-sections-know-which-section-can-save-how-much-tax-here-is-all-about-income-tax-law-to-understand-budget-speech-articleshow-89141099-cms.md')
El contenido ahora parece más limpio, pero algunas secciones son innecesarias, especialmente aquellas con encabezados vacíos. Para solucionar este problema, escribamos una función que pase una sección solo si tanto el encabezado como el contenido no están vacíos y el encabezado no está en la lista ('navegación principal', 'navegación', 'pie de página').
def pass_section(section):
# List of headers to ignore based on experiments
headers_to_ignore = ('main navigation', 'navigation', 'footer', 'advertisement')
# Check if the header is not in the ignore list and both header and content are non-empty
if section('header').lower() not in headers_to_ignore and section('header').strip() and section('content').strip():
return True
return False
#storing everything in passed sections
passed_sections = ()
import os
# Iterate through all Markdown files in the folder
for filename in os.listdir('incometax_documents'):
if filename.endswith('.md'):
file_path = os.path.join('incometax_documents', filename)
# Extract sections from the current Markdown file
sections = read_markdown_file(file_path)
passed_sections.extend(sections)
¡El contenido parece organizado y limpio ahora! y todas las secciones se almacenan en pass_sections.
Nota: Es posible que necesite fragmentación según el contenido, ya que el límite de tokens para el modelo de incrustación es 512. Pero, dado que las secciones son pequeñas en mi caso, lo omitiré. Pero todavía puedes comprobar el computadora portátilpara fragmentar código.
Selección de modelo: elección de los modelos de generación e integración adecuados
Usaremos código abierto multilingüe-E5 como nuestro modelo de incrustación y Airavata por ai4Bharataun LLM índico que es una versión adaptada a las instrucciones de Hathi abiertoun modelo de parámetros 7B por <a target="_blank" href="https://www.sarvam.ai/” target=”_blank” rel=”nofollow noopener”>Sarvam AYo, basado en Llama2 y entrenado en hindi, inglés y hinglish como modelo de generación.
¿Por qué elegí multilingüe-e5-base ¿Como modelo de incrustación? Según su página Hugging Face, admite 100 idiomas, aunque el rendimiento para idiomas de bajos recursos puede variar. Descubrí que funciona razonablemente bien para hindi. Para una mayor precisión, BGE M3 es una opción, pero requiere muchos recursos. Las incorporaciones de OpenAI también podrían funcionar, pero por ahora nos quedamos con las soluciones de código abierto. Por lo tanto, E5 es una opción liviana y efectiva. ¿Por qué Airavata? Aunque los LLM gigantes como GPT 3.5 podrían hacer el trabajo, digamos que quería probar algo de código abierto e indio.
Configurar la tienda de vectores
Elegí Croma DB ya que podría usarlo en Google Collab sin ningún alojamiento y es bueno para experimentar. Pero también puedes usar las tiendas de vectores de tu elección. Así es como lo instalas.
!pip install chromadb
Luego podemos iniciar el cliente chromaDb con los siguientes comandos
import chromadb
chroma_client = chromadb.Client()
Esta forma de iniciar Chroma DB crea una instancia en memoria de Chroma. Esto es útil para pruebas y desarrollo, pero no se recomienda para uso en producción. Para producción, debe alojarlo. Consulte su documentación para más detalles.
A continuación, necesitamos crear una tienda de vectores. Afortunadamente, Chroma DB ofrece soporte integrado para transformadores de oraciones de código abierto. Aquí se explica cómo usarlo:
from chromadb.utils import embedding_functions
#initializing embedding model
sentence_transformer_ef = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="intfloat/multilingual-e5-base")
#creating a collection
collection = chroma_client.create_collection(name="income_tax_hindi", embedding_function= sentence_transformer_ef, metadata={"hnsw:space": "cosine"})
Usamos metadata={“hnsw:space”: “cosine”} porque la distancia predeterminada de ChromaDB es euclidiana, pero normalmente se prefiere la distancia coseno para propósitos de RAG.
En chromaDb, no podemos crear una colección con el mismo nombre si ya existe. Entonces, mientras experimentas, es posible que necesites eliminar la colección para recrearla. Aquí tienes el comando para eliminarla:
# command for deletion
chroma_client.delete_collection(name="income_tax_hindi")
Ingestión y recuperación de documentos
Ahora que hemos almacenado los datos en pass_sections, es hora de ingerir este contenido en ChromaDB. También incluiremos metadatos e ID. Los metadatos son opcionales, pero como tenemos encabezados, mantengámoslos para agregar contexto.
#ingestion documents
collection.add(
documents=(section('content') for section in passed_sections),
metadatas = ({'header': section('header')} for section in passed_sections),
ids=(str(i) for _ in range(len(passed_sections)))
)
#apparently we need to pass some ids to documents in chroma db, hence using id
Ya es hora, comencemos a consultar la tienda de vectores.
docs = collection.query(
query_texts=("सेक्शन 80 C की लिमिट क्या होती है"),
n_results=3
)
print(docs)
Como puede ver, tenemos documentos relevantes basados en distancias de cosenos. Intentemos generar una respuesta usando esto. Para eso, necesitaríamos un LLM.
Generación de respuestas usando Airavata
Como se mencionó, usaremos Airavta y, dado que es de código abierto, usaremos transformadores y técnicas de cuantificación para cargar el modelo. Puede consultar más sobre las formas de cargar LLM de código abierto aquí y aquí. Se necesita un entorno de GPU T4 en colaboración para ejecutar esto.
Comencemos por instalar las bibliotecas relevantes.
!pip install bitsandbytes>=0.39.0
!pip install --upgrade accelerate transformers
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
device = "cuda" if torch.cuda.is_available() else "cpu"
print(device)
# it should print Cuda
Aquí está el código para cargar el modelo cuantificado.
model_name = "ai4bharat/Airavata"
tokenizer = AutoTokenizer.from_pretrained(model_name, padding_side="left")
tokenizer.pad_token = tokenizer.eos_token
quantization_config = BitsAndBytesConfig(load_in_8bit=True)
model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=quantization_config, torch_dtype=torch.bfloat16)
El modelo se ha ajustado para seguir instrucciones y funciona mejor cuando las instrucciones están en el mismo formato que los datos de entrenamiento. Entonces escribiremos una función para organizar todo en un formato adecuado.
Las funciones siguientes pueden parecer abrumadoras, pero son de la página oficial de la modelo Hugging Face. Estas funciones están disponibles para la mayoría de los modelos de código abierto, así que no se preocupe si no las comprende completamente.
def create_prompt_with_chat_format(messages, bos="", eos="", add_bos=True):
formatted_text = ""
for message in messages:
if message("role") == "system":
formatted_text += "<|system|>\n" + message("content") + "\n"
elif message("role") == "user":
formatted_text += "<|user|>\n" + message("content") + "\n"
elif message("role") == "assistant":
formatted_text += "<|assistant|>\n" + message("content").strip() + eos + "\n"
else:
raise ValueError(
"Tulu chat template only supports 'system', 'user' and 'assistant' roles. Invalid role: {}.".format(
message("role")
)
)
formatted_text += "<|assistant|>\n"
formatted_text = bos + formatted_text if add_bos else formatted_text
return formatted_text
Para inferencia, usaremos esta función.
def inference(input_prompts, model, tokenizer):
input_prompts = (
create_prompt_with_chat_format(({"role": "user", "content": input_prompt}), add_bos=False)
for input_prompt in input_prompts
)
encodings = tokenizer(input_prompts, padding=True, return_tensors="pt")
encodings = encodings.to(device)
with torch.inference_mode():
outputs = model.generate(encodings.input_ids, do_sample=False, max_new_tokens=1024)
output_texts = tokenizer.batch_decode(outputs.detach(), skip_special_tokens=True)
input_prompts = (
tokenizer.decode(tokenizer.encode(input_prompt), skip_special_tokens=True) for input_prompt in input_prompts
)
output_texts = (output_text(len(input_prompt) :) for input_prompt, output_text in zip(input_prompts, output_texts))
return output_texts
Ahora la parte interesante: solicitar generar la respuesta. Aquí, creamos un mensaje que indica al modelo de lenguaje que genere respuestas basadas en pautas específicas. Las instrucciones son simples: primero, el modelo lee y comprende la pregunta, luego revisa el contexto proporcionado. Utiliza esta información para elaborar una respuesta clara, concisa y precisa. Si lo miras detenidamente, esta es la versión hindi del mensaje típico de RAG.
Las instrucciones están en hindi porque el modelo Airavta ha sido ajustado para seguir las instrucciones dadas en hindi. Puedes leer más sobre su formación. aquí.
prompt=""'आप एक बड़े भाषा मॉडल हैं जो दिए गए संदर्भ के आधार पर सवालों का उत्तर देते हैं। नीचे दिए गए निर्देशों का पालन करें:
1. **प्रश्न पढ़ें**:
- दिए गए सवाल को ध्यान से पढ़ें और समझें।
2. **संदर्भ पढ़ें**:
- नीचे दिए गए संदर्भ को ध्यानपूर्वक पढ़ें और समझें।
3. **सूचना उत्पन्न करना**:
- संदर्भ का उपयोग करते हुए, प्रश्न का विस्तृत और स्पष्ट उत्तर तैयार करें।
- यह सुनिश्चित करें कि उत्तर सीधा, समझने में आसान और तथ्यों पर आधारित हो।
### उदाहरण:
**संदर्भ**:
"नई दिल्ली भारत की राजधानी है और यह देश का प्रमुख राजनीतिक और प्रशासनिक केंद्र है। यह शहर ऐतिहासिक स्मारकों, संग्रहालयों और विविध संस्कृति के लिए जाना जाता है।"
**प्रश्न**:
"भारत की राजधानी क्या है और यह क्यों महत्वपूर्ण है?"
**प्रत्याशित उत्तर**:
"भारत की राजधानी नई दिल्ली है। यह देश का प्रमुख राजनीतिक और प्रशासनिक केंद्र है और ऐतिहासिक स्मारकों, संग्रहालयों और विविध संस्कृति के लिए जाना जाता है।"
### निर्देश:
अब, दिए गए संदर्भ और प्रश्न का उपयोग करके उत्तर दें:
**संदर्भ**:
{docs}
**प्रश्न**:
{query}
उत्तर:'''
Pruebas y evaluación
Combinando todo esto la función se convierte en:
def generate_answer(query):
docs = collection.query(
query_texts=(query),
n_results=3
) #taking top 3 results
docs = (doc for doc in docs('documents')(0))
docs = "\n".join(docs)
formatted_prompt = prompt.format(docs = docs,query = query)
answers = inference((formatted_prompt), model, tokenizer)
return answers(0)
Probémoslo para algunas preguntas:
questions = (
'सेक्शन 80डीडी के तहत विकलांग आश्रित के लिए कौन से मेडिकल खर्च पर टैक्स छूट मिल सकती है?',
'क्या सेक्शन 80यू और सेक्शन 80डीडी का लाभ एक साथ उठाया जा सकता है?',
'सेक्शन 80 C की लिमिट क्या होती है?'
)
for question in questions:
answer = generate_answer(question)
print(f"Question: {question}\nAnswer: {answer}\n")
#OUTPUT
Question: सेक्शन 80डीडी के तहत विकलांग आश्रित के लिए कौन से मेडिकल खर्च पर टैक्स छूट मिल सकती है?
Answer: आश्रित के लिए टैक्स छूट उन खर्चों पर उपलब्ध है जो 40 फीसदी से अधिक विकलांगता वाले व्यक्ति के लिए आवश्यक हैं। इन खर्चों में अस्पताल में भर्ती होना, सर्जरी, दवाएं और चिकित्सा उपकरण शामिल हैं।
Question: क्या सेक्शन 80यू और सेक्शन 80डीडी का लाभ एक साथ उठाया जा सकता है?
Answer: नहीं।
Question: सेक्शन 80 C की लिमिट क्या होती है?
Answer: सेक्शन 80सी की सीमा 1.5 लाख रुपये है।
¡Buenas respuestas! También puedes intentar experimentar con indicaciones para obtener respuestas breves o detalladas o cambiar el tono del modelo. Me encantaría ver tus experimentos.
¡Ese es el final del blog! Espero que lo hayas disfrutado. En esta publicación, tomamos información relacionada con el impuesto sobre la renta de un sitio web, la ingerimos en ChromaDB utilizando un transformador multilingüe de código abierto y generamos respuestas con un LLM Indic de código abierto.
Estaba un poco inseguro sobre qué detalles incluir, pero intenté mantenerlo conciso. Si desea obtener más información, no dude en consultar mi repositorio de GitHub. Me encantaría escuchar tus comentarios, ya sea que creas que se debería haber incluido algo más o si esto estuvo bien tal como está. ¡Hasta pronto, o como decimos en hindi, फिर मिलेंगे!
Conclusión
El desarrollo de una cartera de RAG adaptada a los idiomas indios demuestra las crecientes capacidades de los LLM indios para abordar necesidades complejas y multilingües. Los LLM índicos permiten a las organizaciones procesar hindi y otros documentos regionales con mayor precisión, garantizando la accesibilidad a la información en diversos orígenes lingüísticos. A medida que refinemos estos modelos, el impacto de los LLM índicos en las aplicaciones en idiomas locales solo aumentará, proporcionando nuevas vías para mejorar la comprensión, la recuperación y la generación de respuestas en los idiomas nativos. Esta innovación marca un importante paso adelante para el procesamiento del lenguaje natural en la India y más allá.
Conclusiones clave
- El uso de incrustaciones multilingües de e5 permite un manejo eficaz de la búsqueda y la comprensión de consultas en hindi.
- Los pequeños LLM de código abierto como Airavata, optimizados para hindi, permiten respuestas precisas y culturalmente relevantes sin necesidad de grandes recursos computacionales.
- ChromaDB simplifica el almacenamiento y la recuperación de vectores, lo que facilita la gestión de datos multilingües en memoria y aumenta la velocidad de respuesta.
- El enfoque aprovecha modelos y herramientas de código abierto, lo que reduce la dependencia de API patentadas de alto costo y al mismo tiempo logra un rendimiento confiable.
- Los LLM índicos permiten una recuperación y un análisis más eficaces de documentos en idioma indio, lo que mejora la accesibilidad al idioma local y las capacidades de PNL.
Preguntas frecuentes
R. Utilice un entorno de GPU T4 en Google Colab para obtener un rendimiento óptimo con el modelo LLM y el almacén de vectores. Esta configuración maneja modelos cuantificados y requisitos de procesamiento pesados de manera eficiente.
R. Sí, si bien este ejemplo utiliza hindi, puede ajustarlo para otros idiomas admitidos por modelos de integración multilingüe y LLM adaptados adecuadamente.
R. Se recomienda ChromaDB para operaciones en memoria en Colab, pero otras bases de datos vectoriales como Pinecone o Faiss también son compatibles, especialmente en producción.
R. Usamos E5 multilingüe para incrustaciones y Airavata para generación de texto.
E5 admite varios idiomas y Airavata está optimizado para hindi, lo que los hace adecuados para nuestra aplicación basada en hindi.
Los medios que se muestran en este artículo no son propiedad de Analytics Vidhya y se utilizan a discreción del autor.