• Calgary AB Canada

Creación de soluciones de IA confidenciales y seguras para la educación, la ingeniería y los negocios

published: 31 de diciembre de 2024 a las 15:41

Sucedió que, después de mi artículo de la semana pasada sobre el Asistente de IA Local, discutí las oportunidades de la IA con un amigo. La época navideña es un buen momento para pasar con amigos y familiares, y él planteó una pregunta sobre el uso de ChatGPT y la seguridad de sus datos.

Todos conocemos ChatGPT. OpenAI también ofrece algunas API que permiten integrar la funcionalidad de chat en tus aplicaciones, por ejemplo, como un chatbot. Pero mi amigo vio dos problemas:

  • Costos de entrenamiento: Para que un chatbot sea útil, debe trabajar con datos de la empresa (por ejemplo, horarios de operación, precios, etc.), lo cual implica entrenarlo. Todos saben que entrenar grandes modelos de lenguaje (LLM o IA) cuesta mucho dinero.
  • Seguridad de los datos: Estaba dispuesto a usar sus propios datos para su chatbot, pero no se sentía cómodo con que OpenAI u otra empresa incorporaran ese conocimiento en sus grandes modelos de lenguaje. Por lo general, los proveedores de IA necesitan más datos para entrenar sus modelos, y él no quiere que sus datos terminen en el producto de otra persona.

En ese momento me di cuenta de que, a pesar de la popularidad de la IA, la gente que no trabaja con estas tecnologías a menudo no tiene suficiente información sobre cómo usarlas. Por eso, después de responder a sus preguntas, decidí publicar mis respuestas.

Cómo enseñar a la IA a usar tus datos

Supongamos que tienes algún tipo de base de datos: tal vez la sección de preguntas frecuentes (FAQ) en tu sitio web, pruebas de conocimiento para estudiantes, datos de soporte técnico o datos de ingeniería como estándares. Si decides entrenar un modelo con estos datos, te encontrarás con tres problemas:

  • Tiempo y dinero: Entrenar puede ser costoso.
  • Dificultad para actualizar datos: Una vez que entrenas tu modelo, no puedes añadir o cambiar fácilmente su conocimiento sin volver a entrenarlo; por ejemplo, no podrías cambiar la respuesta si descubrieras que está mal (mi amigo no pensó en esto).
  • Evolución rápida de la IA: La IA está avanzando a gran velocidad. Si decides cambiar a un modelo más eficiente el próximo mes, tendrás que volver a entrenarlo.

Sin embargo, realmente no necesitas entrenar el modelo. Puedes usar la tecnología RAG (Retrieval-Augmented Generation). Este enfoque implica que no guardas tu conocimiento dentro de la LLM.

En su lugar, haces lo siguiente:

  1. Almacenas tus datos, por ejemplo las entradas de la sección FAQ (preguntas y respuestas), en una base de datos que admita búsqueda semántica (a menudo llamada “base de datos vectorial”).
  2. Cuando un usuario hace una pregunta, utilizas la LLM para convertir la pregunta en un embedding (un vector numérico).
  3. Realizas una búsqueda semántica en tu base de datos vectorial para encontrar las entradas de FAQ más relevantes.
  4. Proporcionas esas entradas relevantes como contexto a la LLM.
  5. La LLM utiliza ese contexto para formular la respuesta.

Las ventajas de este enfoque son:

  • Escalabilidad: Puedes actualizar tu información en cualquier momento simplemente añadiendo preguntas y respuestas nuevas a la base de datos, o modificando las ya existentes.
  • Precisión: Reduces las “alucinaciones” del modelo al basar sus respuestas en tus datos de negocio.
  • Flexibilidad: Puedes cambiar fácilmente el modelo que uses (puede ser un modelo local o cualquier proveedor de LLM — OpenAI, Anthropic o cualquier otro que prefieras).

La tecnología RAG responde la primera pregunta de mi amigo sobre cómo enseñar a la IA a usar sus datos.

Seguridad: modelos alojados vs. modelos locales

La segunda pregunta de mi amigo era sobre la seguridad de los datos, que puede ser simple o complicada, según tu punto de vista. Tienes dos opciones principales:

  • Firmar un acuerdo con un proveedor de LLM: confías en que gestionarán tus datos responsablemente y evitarán filtraciones.
  • Usar una LLM local en tu propio servidor: tienes control total sobre tus datos, minimizando el riesgo de que se difundan.

A primera vista, usar un proveedor de LLM parece preferible. Tienen hardware moderno, gran potencia de cómputo y pueden usar modelos enormes que ofrecen resultados de primera categoría. Sin embargo, para muchas tareas empresariales reales —como responder 5,000 a 10,000 preguntas— tu modelo no necesita saber todo sobre el mundo. Solo necesita entender la pregunta y generar la respuesta adecuada.

Esto significa que es posible usar modelos más pequeños que no exigen muchos recursos. Incluso un modelo con 1 a 3 mil millones de parámetros, que podrías ejecutar en tu laptop, funcionaría bastante bien, mientras que los modelos con 7 u 12 mil millones de parámetros pueden rendir extremadamente bien en un servidor. Los modelos con 22 o 70 mil millones probablemente serán demasiado para muchas tareas. Por supuesto, si planeas ejecutarlo localmente para cargas de trabajo empresariales reales, tendrás que calcular cuántas solicitudes por hora tendrás y luego asignar la capacidad del servidor en consecuencia. También tienes la opción de alquilar servidores en la nube (por ejemplo, AWS), lo cual aún puede percibirse como más seguro que compartir tus datos con un proveedor de LLM.

Ejemplo: Retrieval-Augmented Generation (RAG) con FAISS y una LLM pequeña

A continuación, puedes ver un fragmento de código en Python que ilustra cómo se podría implementar el enfoque RAG. Es un ejemplo sencillo para demostrar la idea; no lo copies directamente en soluciones reales.


#!/usr/bin/env python
# coding: utf-8

"""
Example: Retrieval-Augmented Generation (RAG) with FAISS and a Small LLM

This code snippet illustrates:
1. How to embed a small Q&A dataset (e.g., FAQs) using a BERT-like model.
2. How to index and query those embeddings with FAISS for semantic search.
3. How to use a small LLM (T5 in this case) to generate a final answer
   based on the retrieved context and an additional note.

Dependencies:
- transformers
- torch
- faiss
- numpy

Important:
This is a simplified example. In a production setting, you'll want to handle
larger datasets, more robust error checking, and optimize for speed.
"""

import torch
from transformers import AutoTokenizer, AutoModel, AutoModelForSeq2SeqLM
import numpy as np
import faiss

# -----------------------------------------------------
# Step 1: Load a Transformer Model and Tokenizer
# -----------------------------------------------------
model_name = "distilbert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)

# -----------------------------------------------------
# Step 2: Prepare a Small Q&A Dataset
# -----------------------------------------------------
# Example data (could be from Education, Engineering, or Business)
business_questions = [
    "What are your working hours?",
    "Where are you located?",
]
business_answers = [
    "Our working hours are 9 AM to 5 PM from Monday to Friday.",
    "Our address is 123456 Main Street, SE, Calgary, Alberta, Canada.",
]

# -----------------------------------------------------
# Step 3: Encode the Questions into Embeddings
# -----------------------------------------------------
data_embeddings = []
for question in business_questions:
    inputs = tokenizer(
        question, 
        return_tensors="pt", 
        truncation=True, 
        padding=True, 
        max_length=128)

    with torch.no_grad():
        # Use mean pooling of the last hidden state as the embedding
        embedding = model(**inputs).last_hidden_state.mean(dim=1)
    data_embeddings.append(embedding.squeeze().numpy())

data_embeddings = np.array(data_embeddings)

# -----------------------------------------------------
# Step 4: Create a FAISS Index for Semantic Search
# -----------------------------------------------------
dimension = data_embeddings.shape[1]  # e.g., 768 for DistilBERT
index = faiss.IndexFlatL2(dimension)  # L2 distance
index.add(data_embeddings)

# Store the original questions/answers for retrieval
stored_datas = [{"question": q, "answer": a} for q, \
    a in zip(business_questions, business_answers)]

# (Optional) Save the index for later reuse
faiss.write_index(index, "data_index.faiss")

# -----------------------------------------------------
# Step 5: Define a Function to Query the FAISS Index
# -----------------------------------------------------
def query_data(query, top_k=3):
    """
    Encode the query into an embedding, search the FAISS index,
    and return the top-k matched Q&A pairs.
    """
    # Convert query to embedding
    inputs = tokenizer(query, return_tensors="pt", truncation=True, padding=True, \
        max_length=128)

    with torch.no_grad():
        query_embedding = model(**inputs).last_hidden_state.mean(dim=1).squeeze().numpy()

    # Search for top_k matches
    distances, indices = index.search(np.array([query_embedding]), k=top_k)

    # Retrieve and rank results
    results = []
    for i in range(len(indices[0])):
        idx = indices[0][i]
        results.append({
            "question": stored_datas[idx]["question"],
            "answer": stored_datas[idx]["answer"],
            "distance": distances[0][i]
        })

    # FAISS already sorts by distance; no need to resort unless you want custom logic
    results.sort(key=lambda x: x["distance"])
    return results

# -----------------------------------------------------
# Step 6: Load a Small LLM for Response Generation
# -----------------------------------------------------
response_model_name = "google/flan-t5-large"
response_tokenizer = AutoTokenizer.from_pretrained(response_model_name)
response_model = AutoModelForSeq2SeqLM.from_pretrained(response_model_name)

# -----------------------------------------------------
# Step 7: Generate a Context-Aware Response
# -----------------------------------------------------
def generate_response_with_llm(user_query, top_results):
    """
    Use a small, modern LLM to generate a conversational response
    based on the user query and the retrieved knowledge.
    """

    # Additional domain/business-specific info
    context_note = (
        "Additional note: You represent PhotoInPrint, a service providing "
        "professional Fine Art Photography Printing using high-quality papers "
        "like Ilford, Hahnemühle, and Red River. Orders can be placed online "
        "from anywhere, including the United States. No photobooks or acrylic.\n\n"
    )

    # Construct context from top retrieved results
    context = context_note
    context += "Analyze the following information to answer the user's question:\n"
    for i, result in enumerate(top_results, 1):
        context += f"{i}. Question: {result['question']} - Answer: {result['answer']}\n"

    # Instruction for the model
    context += (
        f"\nYour task: Using only the answers above and the additional note, "
        f"generate a clear, detailed, and helpful response to the user's question: "
        f"\"{user_query}\".\n"
        "Combine relevant answers where appropriate to create a smooth and "
        "comprehensive response. Do not use information not explicitly provided. "
        "If no relevant information is found, politely inform the user and ask for "
        "clarification.\n"
    )

    # Tokenize the input context
    inputs = response_tokenizer(context, return_tensors="pt", truncation=True, \
        max_length=512)

    # Generate the response
    outputs = response_model.generate(
        **inputs,
        max_length=140,
        temperature=0.7,
        top_p=0.9,
        do_sample=True
    )

    # Decode and return the response text
    response = response_tokenizer.decode(outputs[0], skip_special_tokens=True).strip()
    return response

# -----------------------------------------------------
# Step 8: Example Usage
# -----------------------------------------------------
if __name__ == "__main__":
    user_query = "Are you working at 2pm on Monday?"
    top_results = query_data(user_query, top_k=3)
    final_response = generate_response_with_llm(user_query, top_results)
    print("User Query:", user_query)
    print("Generated Response:\n", final_response)

Cómo funciona:

  1. Embedding: Usamos un modelo parecido a BERT (DistilBERT) para convertir cada “pregunta” de nuestra base de conocimiento en vectores de embeddings.
  2. Indexación: Guardamos estos embeddings en un índice FAISS para lograr una búsqueda semántica rápida.
  3. Recuperación: Cuando un usuario envía una consulta, transformamos esa consulta en un embedding y recuperamos las entradas más relevantes de la base de datos mediante FAISS.
  4. Generación de la respuesta: Luego, pasamos la consulta del usuario y la información recuperada a un modelo T5 pequeño (google/flan-t5-large) para generar una respuesta final que haga referencia únicamente al contexto proporcionado.

Las soluciones basadas en RAG, combinadas con LLM locales o en la nube controlada, ofrecen una manera segura, rentable y flexible de incorporar IA en diversos dominios, sin los dolores de cabeza del entrenamiento continuo ni los riesgos de exposición de datos.

En la implementación de IA en entornos reales, sí se necesita algo de trabajo de ingeniería, especialmente para configurar la base de datos vectorial, conectarla con tu base de conocimiento y optimizar la ejecución. No obstante, sigue siendo factible incluso para empresas relativamente pequeñas.

En resumen:

El enfoque de Retrieval-Augmented Generation te permite mantener flexibilidad al cambiar de una LLM a otra, reducir costos, guardar el conocimiento del negocio en una base de datos sencilla y actualizar ese conocimiento en cualquier momento. Esta es una forma sencilla y económica para que Educación, Ingeniería y Negocios integren la IA en las operaciones y soluciones diarias.