Herramientas de usuario

Herramientas del sitio


inteligencia_artificial:langchain:evaluacion

Evaluación

Es importante saber cómo evaluar una aplicación con modelos LLM, para poder medir de algún modo con qué modelo funciona mejor, o con que retriever, o cualquier otro cambio.

Estas aplicaciones que usan LLM, en realidad son “chains” con concatenaciones de éstos, y es posible usar a su vez modelos LLM para evaluar nuestra aplicación.

Evaluación manual

Consiste en crear por mi parte unos ejemplos de pregunta - respuesta, y después hago que el modelo cree otros ejemplos de forma automática.
La idea es que el modelo responda a estas preguntas y ver si su respuesta es correcta: en las pruebas que he hecho deben ser igual que las mías, y en las generadas de forma automática, deben ser correctas.

Partimos del ejempo en el que obtenemos los datos de un CSV:

# Ejemplo en el que cargaremos información de un documento CSV y le preguntaremos al modelo
# cuestiones acerca de esta información
 
from langchain_ollama import ChatOllama
from langchain_ollama import OllamaEmbeddings # Modelo embedding para crear las representaciones numéricas de los datos.
from langchain.chains.retrieval_qa.base import RetrievalQA  # Sirve para recuperar información de documentos
from langchain_community.document_loaders import CSVLoader    # Para cargar informaciónd de documentos CSV
from langchain_community.vectorstores import DocArrayInMemorySearch   # Forma de guardar la información en un "vector store" ó almacén de datos. Muy sencilla y no requiere conexión a base de datos 
from langchain.indexes import VectorstoreIndexCreator   # Para crear un índice que nos ayudará a crear un almacén de datos de forma fácil
import pprint
 
# Cargamos el documento CSV
file = 'profesores.csv'
loader = CSVLoader(file_path=file, encoding="utf-8")
data = loader.load()
 
# Especificamos el modelo que generará los "embeddings"
embeddings = OllamaEmbeddings (model="llama3.2")
 
# Creamos el índice especificando su clase (tipo de almacén de datos) y la lista de "loaders" de donde extraer la información, que en nuestro caso sólo hay uno
index = VectorstoreIndexCreator (vectorstore_cls=DocArrayInMemorySearch, embedding=embeddings).from_loaders([loader])
 
# Definimos el modelo con el que vamos a trabajar
llm = ChatOllama(
    model = "llama3.2",
    temperature = 0.3,
    verbose = True
)
 
# Definimos la interfaz con el vectorstore
qa = RetrievalQA.from_chain_type(
    llm = llm,
    chain_type="stuff",
    retriever=index.vectorstore.as_retriever(),
    verbose=True,
    chain_type_kwargs={
        "document_separator": "<<<<>>>>",
    }
)

A continuación realizamos la comprobación “manual”, para lo cual:

  1. Generamos nuestros propios ejemplos.
  2. Usamos el chain QAGenerateChain que genera ejemplos de pregunta - respuesta a partir del LLM indicado.
  3. Añadimos a nuestros ejemplos hechos de forma manual, los generados automáticamente.
  4. A partir del retriever (RetrievarQA), ejecutamos las consultas de todos los ejemplos.
  5. Mostramos las respuestas para comprobar si el modelo a resuelto las consultas correctamente, como era esperado.
# Evaluación manual: Creo unos ejemplos de pregunta - respuesta y después creo otros de forma automática con LLM.
#    Hago que el modelo responda a la misma pregunta (mía o de las que ha creado él) para ver directamente si acierta o no.
 
ejemplos = [
    {
        "query": "¿Qué clase impartió Ana María de Santiago Nocito \
        el 2/15/2024?",
        "answer": "RCP básica con DESA y fármacos."
    },
    {
        "query": "¿Cuántas clases ha impartido en total Luis España Barrio?",
        "answer": "Luis España Barrio ha impartido 11 clases."
    }
]
 
from langchain.evaluation.qa import QAGenerateChain
import langchain
 
langchain.debug = True
 
chain_generador_ejemplos = QAGenerateChain.from_llm(llm)
nuevos_ejemplos = chain_generador_ejemplos.apply_and_parse(
    [{"doc": t} for t in data[:5]]
)
 
ejemplos += nuevos_ejemplos
 
pprint.pprint(ejemplos[0])
#respuesta=qa.run(ejemplos[0]["qa_pairs"]["query"])
respuesta=qa.run(ejemplos[0]["query"])
 
langchain.debug = False
 
pprint.pprint(respuesta)

Evaluación automática

Podemos programar la automatización de la comprobación de las respuestas.
Partimos de nuevo de la carga del documento CSV y la creación del vectorstore y el retrieval:

# Ejemplo en el que cargaremos información de un documento CSV y le preguntaremos al modelo
# cuestiones acerca de esta información
 
from langchain_ollama import ChatOllama
from langchain_ollama import OllamaEmbeddings # Modelo embedding para crear las representaciones numéricas de los datos.
from langchain.chains.retrieval_qa.base import RetrievalQA  # Sirve para recuperar información de documentos
from langchain_community.document_loaders import CSVLoader    # Para cargar informaciónd de documentos CSV
from langchain_community.vectorstores import DocArrayInMemorySearch   # Forma de guardar la información en un "vector store" ó almacén de datos. Muy sencilla y no requiere conexión a base de datos 
from langchain.indexes import VectorstoreIndexCreator   # Para crear un índice que nos ayudará a crear un almacén de datos de forma fácil
import pprint
 
# Cargamos el documento CSV
file = 'profesores.csv'
loader = CSVLoader(file_path=file, encoding="utf-8")
data = loader.load()
 
# Especificamos el modelo que generará los "embeddings"
embeddings = OllamaEmbeddings (model="llama3.2")
 
# Creamos el índice especificando su clase (tipo de almacén de datos) y la lista de "loaders" de donde extraer la información, que en nuestro caso sólo hay uno
index = VectorstoreIndexCreator (vectorstore_cls=DocArrayInMemorySearch, embedding=embeddings).from_loaders([loader])
 
# Definimos el modelo con el que vamos a trabajar
llm = ChatOllama(
    model = "llama3.2",
    temperature = 0.3,
    verbose = True
)
 
# Definimos la interfaz con el vectorstore
qa = RetrievalQA.from_chain_type(
    llm = llm,
    chain_type="stuff",
    retriever=index.vectorstore.as_retriever(),
    verbose=True,
    chain_type_kwargs={
        "document_separator": "<<<<>>>>",
    }
)

Ahora vamos a realizar la comprobación automática del software:

  1. En primer lugar volveremos a generar nuestros propios ejemplos.
  2. Volvemos a generar de forma automática ejemplos pregunta-respuesta con QAGenerateChain
  3. Ahora deberíamos juntar todos los ejemplos, como necesitamos que tengan un formato concreto, lo ajustamos.
  4. A partir del retriever, usamos la función apply(), que ejecuta las consultas ('query') que le pasamos como argumento, de forma que devuelve por cada una un diccionario con 3 elementos:
    • query: consulta realizada.
    • answer: respuesta que se generó anteriormente, o que hicimos nosotros de forma manual.
    • result: respuesta gnerada ahora por el modelo especificado.
  5. Ahora con la función evaluate() del chain QAEvalChain le metemos los datos obtenidos, el LLM especificado compara los answer con los result, determinando que la respuesta es correcta correct si son los mismos, o incorrecta incorrect si son diferentes.
# Evaluación automática: Creo unos ejemplos de pregunta - respuesta y después creo otros de forma automática con LLM.
#    El modelo responda a la mismas preguntas (mías o de las que ha creado él) y deberá clasificar las respuestas en correctas o incorrectas.
 
# Creo mis propios ejemlos de forma manual
ejemplos = [
    {
        "query": "¿Qué clase impartió Ana María de Santiago Nocito el 2/15/2024?",
        "answer": "RCP básica con DESA y fármacos."
    },
    {
        "query": "¿Cuántas clases ha impartido en total Luis España Barrio?",
        "answer": "Luis España Barrio ha impartido 11 clases."
    }
]
 
# Dependencias necesarias para la evaluación
from langchain.evaluation.qa import QAGenerateChain # Generación automática de pares pregunta-respuesta
from langchain.evaluation.qa import QAEvalChain     # Evaluación automática de respuestas
 
#Definimos el chain QAGenerateChain y lo usamos para definir pares pregunta-respuestas de forma automática
chain_generador_ejemplos = QAGenerateChain.from_llm(llm)
nuevos_ejemplos = chain_generador_ejemplos.apply_and_parse(
    [{"doc": t} for t in data[:5]]
)
 
# Junto todos los ejemplos, ajustándolos a la forma query:answer, que es el formato necesario para usar después la función "apply"
ejemplos = ejemplos + [
    {
        "query": ejemplo['qa_pairs']["query"],
        "answer": ejemplo['qa_pairs']["answer"]
    } for ejemplo in nuevos_ejemplos
]
 
# Ejecuto las consultas de los ejemplos y obtnego: la consulta (query), la respuesta del ejemlop, ya sea la mía o la que generó el modelo (answwer) y la que ha generado ahora (result)
predicciones = qa.apply(ejemplos)
 
# Evaluamos las respuestas, 'evaluate' compara a través del LLM especificado la respuesta del ejemlo y la obtenida posteriormente y determina si son 'correct' o 'incorrect'
eval_chain = QAEvalChain.from_llm(llm)
salidas_clasificadas = eval_chain.evaluate(ejemplos, predicciones)
 
# Mostramos las  conclusiones de la evaluación automática
for i, eg in enumerate (ejemplos):
    print(f"Ejemplo {i}: ")
    print("Question: " + predicciones[i]['query'])
    print("Real Answer: " + predicciones[i]['answer'])
    print("Predicted Answer: " + predicciones[i]['result'])
    print("Clasificación de predicción: " + salidas_clasificadas[i]['results'])
    print()
 
print(salidas_clasificadas[0])

En nuestro caso es siempre incorrecto, por el problema que arrastramos cuando vimos los chains QA para preguntar acerca del contenido de documentos: Como cargamos los datos de un CSV, los registros se pasan como “documentos” o “porciones de información” independientes, lo que hace que los pares preguntas-respuesta generadas sean de registros concretos. El problema es que cuando el modelo es consultado, el retriever no funciona correctamente y no recupera los datos correctos. Puede deverse a múltiples factores relacionados con la forma en que funciona el retriever, pero eso ahora mismo excede el objetivo de este texto formativo.

  • usaremos el chain QAEvalChain

Depuración

También es importante saber que hay varias maneras de ver qué está pasando “tras bambalinas”, a la hora de depurar.
Esto se puede hacer de dos Maneras:

  • verbose = true, como parámetro del chain, de modo que se podrá observar qué ocurre “dentro” de ese chain cuando se ejecuta.
# Anteriormente se define qa como un QAChain y data como el contenido de un loaderCSV
from langchain.evaluation.qa import QAGenerateChain
 
chain_generador_ejemplos = QAGenerateChain.from_llm(llm, verbose=True)  # Indicamos que muestre lo que ocurre "dentro" de este chain
ejemplos = chain_generador_ejemplos.apply_and_parse(
    [{"doc": t} for t in data[:5]]
)
 
pprint.pprint(ejemplos[0])
respuesta=qa.run(ejemplos[0]["qa_pairs"]["query"])
 
pprint.pprint(respuesta)
  • Debug Mode: importamos directamente langchain, para poder activar (y desactivar) el modo depuración, que provocará que se muestre TODO lo que ocurre, cada vez que se llama a una función de langchain.
# Anteriormente se define qa como un QAChain y data como el contenido de un loaderCSV
from langchain.evaluation.qa import QAGenerateChain
import langchain  # Dependencia necesaria para entrar en el modo 'debug'
 
langchain.debug = True
chain_generador_ejemplos = QAGenerateChain.from_llm(llm)
ejemplos = chain_generador_ejemplos.apply_and_parse(
    [{"doc": t} for t in data[:5]]
)
 
pprint.pprint(ejemplos[0])
respuesta=qa.run(ejemplos[0]["qa_pairs"]["query"])
 
langchain.debug = False
 
pprint.pprint(respuesta)
inteligencia_artificial/langchain/evaluacion.txt · Última modificación: por alberto

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki