Come utilizzare la riduzione della dimensionalità UMAP per gli incorporamenti per mostrare più domande di valutazione e le loro relazioni con i documenti di origine con Ragas, OpenAI, Langchain e ChromaDB
Retrieval-Augmented Generation (RAG) aggiunge una fase di recupero al flusso di lavoro di un LLM, consentendogli di interrogare dati rilevanti da fonti aggiuntive come documenti privati quando risponde a domande e query (1). Questo flusso di lavoro non richiede formazione costosa o messa a punto di LLM sui documenti aggiuntivi. I documenti vengono suddivisi in frammenti, che vengono poi indicizzati, spesso utilizzando una rappresentazione vettoriale compatta generata da ML (embedding). Gli snippet con contenuti simili saranno vicini gli uni agli altri in questo spazio di incorporamento.
L'applicazione RAG proietta le domande fornite dall'utente nello spazio di incorporamento per recuperare frammenti di documento rilevanti in base alla loro distanza dalla domanda. Il LLM può utilizzare le informazioni recuperate per rispondere alla domanda e per motivare la sua conclusione presentando i frammenti come riferimenti.
La valutazione di una domanda RAG è impegnativa (2). Esistono diversi approcci: da un lato, ci sono metodi in cui la risposta come verità fondamentale deve essere fornita dallo sviluppatore; d'altro canto la risposta (e la domanda) può essere generata anche da un altro LLM. Uno dei più grandi sistemi open source per la risposta supportata da LLM è Ragas (4) (Retrieval-Augmented Generation Assessment), che fornisce
- Metodi per generare dati di test basati sui documenti e
- Valutazioni basate su diverse metriche per valutare i passaggi di recupero e generazione uno per uno e end-to-end.
In questo articolo imparerai
Avvia un notebook e installa i pacchetti Python richiesti
!pip install langchain langchain-openai chromadb renumics-spotlight
%env OPENAI_API_KEY=<your-api-key>
Questo tutorial utilizza i seguenti pacchetti Python:
- Langchain: un framework per integrare modelli linguistici e componenti RAG, rendendo il processo di installazione più fluido.
- Renumics-Spotlight: uno strumento di visualizzazione per esplorare in modo interattivo set di dati ML non strutturati.
- Ragas: un framework che ti aiuta a valutare le tue pipeline RAG
Dichiarazione di non responsabilità: l'autore di questo articolo è anche uno degli sviluppatori di Spotlight.
Puoi utilizzare la tua applicazione RAG, passa alla parte successiva per imparare come valutare, estrarre e visualizzare.
Oppure puoi utilizzare l'applicazione RAG dal ultimo articolo con il nostro set di dati preparato di tutti gli articoli di Formula Uno di Wikipedia. Lì puoi anche inserire i tuoi documenti in una sottocartella “docs/”.
Questo set di dati è basato su articoli di Wikipedia ed è concesso in licenza sotto la licenza Creative Commons Attribuzione-Condividi allo stesso modo. Gli articoli originali e l'elenco degli autori si trovano nelle rispettive pagine di Wikipedia.
Ora puoi usare Langchain DirectoryLoader
per caricare tutti i file dalla sottodirectory docs e dividere i documenti in frammenti utilizzando il file RecursiveCharacterTextSpliter
. Con OpenAIEmbeddings
puoi creare incorporamenti e memorizzarli in un file ChromaDB
come archivio vettoriale. Per la catena stessa puoi utilizzare LangChains ChatOpenAI
e un ChatPromptTemplate
.
IL codice collegato per questo articolo contiene tutti i passaggi necessari e puoi trovare una descrizione dettagliata di tutti i passaggi sopra in l'ultimo articolo.
Un punto importante è che dovresti utilizzare una funzione hash per creare ID per gli snippet ChromaDB
. Ciò consente di trovare gli incorporamenti nel db se si dispone solo del documento con il suo contenuto e metadati. Ciò rende possibile saltare i documenti già esistenti nel database.
import hashlib
import json
from langchain_core.documents import Documentdef stable_hash_meta(doc: Document) -> str:
"""
Stable hash document based on its metadata.
"""
return hashlib.sha1(json.dumps(doc.metadata, sort_keys=True).encode()).hexdigest()
...
splits = text_splitter.split_documents(docs)
splits_ids = (
{"doc": split, "id": stable_hash_meta(split.metadata)} for split in splits
)
existing_ids = docs_vectorstore.get()("ids")
new_splits_ids = (split for split in splits_ids if split("id") not in existing_ids)
docs_vectorstore.add_documents(
documents=(split("doc") for split in new_splits_ids),
ids=(split("id") for split in new_splits_ids),
)
docs_vectorstore.persist()
Per un argomento comune come la Formula 1, è anche possibile utilizzare direttamente ChatGPT per generare domande generali. In questo articolo vengono utilizzati quattro metodi di generazione delle domande:
- GPT4: 30 domande sono state generate utilizzando ChatGPT 4 con il seguente messaggio “Scrivi 30 domande sulla Formula uno”
– Esempio casuale: “Quale squadra di Formula 1 è nota per il logo del cavallino rampante?” - GPT3.5: Altre 199 domande sono state generate con ChatGPT 3.5 con il seguente prompt “Scrivi 100 domande sulla Formula uno” e ripetendo “Grazie, scrivi altre 100 per favore”
– Esempio: “Quale pilota vinse il primo Campionato del mondo di Formula 1 nel 1950?” - Ragas_GPT4: 113 domande sono state generate utilizzando Ragas. Ragas utilizza nuovamente i documenti e il proprio modello di incorporamento per costruire un database vettoriale, che viene poi utilizzato per generare domande con GPT4.
– Esempio: “Puoi dirmi di più sulle prestazioni della vettura di Formula Uno Jordan 198 nel Campionato del Mondo 1998?” - Rags_GPT3.5: 226 domande aggiuntive sono state generate con Ragas: qui utilizziamo GPT3.5
– Esempio: “Quale incidente si è verificato al Gran Premio del Belgio 2014 che ha portato al ritiro di Hamilton dalla gara?”
from ragas.testset import TestsetGeneratorgenerator = TestsetGenerator.from_default(
openai_generator_llm="gpt-3.5-turbo-16k",
openai_filter_llm="gpt-3.5-turbo-16k"
)
testset_ragas_gpt35 = generator.generate(docs, 100)
Le domande e le risposte non sono state riviste o modificate in alcun modo. Tutte le domande sono combinate in un unico dataframe con le colonne id
, question
, ground_truth
, question_by
E answer
.
Successivamente le domande verranno poste al sistema RAG. Per oltre 500 domande, ciò può richiedere del tempo e comportare dei costi. Se poni le domande riga per riga, puoi mettere in pausa e continuare il processo o recuperare da un arresto anomalo senza perdere i risultati ottenuti fino a quel momento:
for i, row in df_questions_answers.iterrows():
if row("answer") is None or pd.isnull(row("answer")):
response = rag_chain.invoke(row("question"))df_questions_answers.loc(df_questions_answers.index(i), "answer") = response(
"answer"
)
df_questions_answers.loc(df_questions_answers.index(i), "source_documents") = (
stable_hash_meta(source_document.metadata)
for source_document in response("source_documents")
)
Non viene memorizzata solo la risposta, ma anche gli ID di origine dei frammenti di documento recuperati e il loro contenuto di testo come contesto:
Fonte: towardsdatascience.com