Ricerca potenziata dalla modalità RAG (Recupero-Risposta-Aumentato) Dalla teoria all’implementazione del sistema LangChain

La modalità RAG (Recupero-Risposta-Aumentato) Un'approfondita ricerca dalla teoria all'implementazione del sistema LangChain

Dalla teoria del paper accademico originale alla sua implementazione in Python con OpenAI, Weaviate e LangChain

Flusso di lavoro generazione-retroperimento

Dalla scoperta che è possibile potenziare i grandi modelli di linguaggio (LLM) con i tuoi dati proprietari, si è discusso su come colmare efficacemente il divario tra le conoscenze generali del LLM e i tuoi dati proprietari. C’è stata molta discussione su quale sia più adatto a questo scopo, se il retraining o la generazione con retroperimento (RAG) (spoiler: sono entrambi).

Questo articolo si concentra innanzitutto sul concetto di RAG e illustra la sua teoria. Successivamente, mostra come è possibile implementare un semplice flusso di lavoro RAG utilizzando LangChain per l’orchestrazione, modelli di linguaggio di OpenAI e un database di vettori Weaviate.

Cos’è la generazione con retroperimento

La generazione con retroperimento (RAG) è il concetto di fornire ai LLM informazioni aggiuntive da una fonte di conoscenza esterna, consentendo loro di generare risposte più accurate e contestualizzate riducendo le “allucinazioni”.

Problema

I LLM all’avanguardia vengono addestrati su grandi quantità di dati per acquisire un ampio spettro di conoscenza generale immagazzinata nei pesi dell’inferenza parametrica della rete neurale. Tuttavia, quando si chiede a un LLM di generare un completamento che richiede conoscenze non incluse nei dati di addestramento, come informazioni nuove, proprietarie o specifiche di dominio, si possono verificare inesattezze fattuali (allucinazioni), come illustrato nella seguente immagine:

La risposta di ChatGPT alla domanda,

È quindi importante colmare il divario tra la conoscenza generale del LLM e il contesto aggiuntivo per aiutare il LLM a generare completamenti più accurati e contestualizzati riducendo le “allucinazioni”.

Soluzione

Tradizionalmente, le reti neurali vengono adattate alle informazioni di dominio specifico o proprietario tramite il retraining del modello. Sebbene questa tecnica sia efficace, è anche intensiva in termini di risorse computazionali, costosa e richiede competenze tecniche, rendendola meno agile nell’adattarsi alle informazioni in continua evoluzione.

Nel 2020, Lewis et al. hanno proposto una tecnica più flessibile chiamata generazione con retroperimento (RAG) nel paper Generazione con retroperimento per attività NLP ad alta conoscenza [1]. In questo paper, i ricercatori hanno combinato un modello generativo con un modulo di retroperimento per fornire informazioni aggiuntive da una fonte di conoscenza esterna che può essere aggiornata più facilmente.

In parole semplici, RAG è ai LLM ciò che un esame aperto è per gli esseri umani. In un esame aperto, agli studenti è permesso portare materiali di riferimento, come libri di testo o appunti, che possono utilizzare per cercare informazioni pertinenti per rispondere a una domanda. L’idea alla base di un esame aperto è che il test si concentra sulle abilità di ragionamento degli studenti anziché sulla loro capacità di memorizzare informazioni specifiche.

In modo simile, la conoscenza fattuale è separata dalla capacità di ragionamento del LLM e viene memorizzata in una fonte di conoscenza esterna, accessibile e aggiornabile facilmente:

  • Conoscenza parametrica: Appresa durante l’addestramento e implicitamente memorizzata nei pesi della rete neurale.
  • Conoscenza non parametrica: Memorizzata in una fonte di conoscenza esterna, come un database di vettori.

(Tra l’altro, questa geniale comparazione non è stata ideata da me. Per quanto ne so, questa comparazione è stata menzionata per la prima volta da JJ durante la competizione Kaggle – LLM Science Exam qui.)

Il flusso di lavoro RAG vanilla è illustrato di seguito:

Flusso di lavoro Retrieval-Augmented Generation
  1. Recupero: La query dell’utente viene utilizzata per recuperare il contesto pertinente da una fonte di conoscenza esterna. A tal fine, la query dell’utente viene incorporata in un modello di embedding nello stesso spazio vettoriale del contesto aggiuntivo nel database vettoriale. Ciò consente di eseguire una ricerca di similarità e di restituire i k oggetti dati più simili dal database vettoriale.
  2. Aumento: La query dell’utente e il contesto ulteriore recuperato vengono inseriti in un modello di prompt.
  3. Generazione: Infine, il prompt con recupero-aumento viene alimentato al LLM.

Implementazione della Retrieval-Augmented Generation utilizzando LangChain

In questa sezione viene implementato un pipeline RAG in Python utilizzando un OpenAI LLM in combinazione con un database vettoriale di Weaviate e un modello di embedding di OpenAI. LangChain viene utilizzato per l’orchestrazione.

Se non conosci LangChain o Weaviate, potresti voler dare un’occhiata ai seguenti due articoli:

Introduzione a LangChain: Guida per principianti alla creazione di applicazioni basate su LLM

Un tutorial su LangChain per creare qualsiasi cosa con modelli di linguaggio di grande dimensione in Python

towardsdatascience.com

Introduzione a Weaviate: Guida per principianti alla ricerca con database vettoriali

Come utilizzare i database vettoriali per la ricerca semantica, la risposta alle domande e la ricerca generativa in Python con OpenAI e…

towardsdatascience.com

Prerequisiti

Assicurati di avere installati i pacchetti Python richiesti:

  • langchain per l’orchestrazione
  • openai per il modello di embedding e LLM
  • weaviate-client per il database vettoriale
#!pip install langchain openai weaviate-client

Inoltre, definisci le tue variabili d’ambiente pertinenti in un file .env nella tua directory principale. Per ottenere una chiave API di OpenAI, hai bisogno di un account OpenAI e poi di “Creare una nuova chiave segreta” nella sezione Chiavi API.

OPENAI_API_KEY="<LA_TUA_CHIAVE_API_OPENAI>"

Quindi, esegui il seguente comando per caricare le variabili d’ambiente pertinenti.

import dotenvdotenv.load_dotenv()

Preparazione

Come passo preparatorio, è necessario preparare un database vettoriale come fonte di conoscenza esterna che contiene tutte le informazioni aggiuntive. Questo database vettoriale viene popolato seguendo questi passaggi:

  1. Raccogli e carica i tuoi dati
  2. Dividi i tuoi documenti in blocchi
  3. Effettua l’embedding e memorizza i blocchi

Il primo passo è raccogliere e caricare i tuoi dati – Per questo esempio, utilizzerai il discorso sullo stato dell’Unione del Presidente Biden dal 2022 come contesto aggiuntivo. Il documento di testo grezzo è disponibile nel repository GitHub di LangChain. Per caricare i dati, puoi utilizzare uno dei tanti DocumentLoader integrati in LangChain. Un Document è un dizionario con testo e metadati. Per caricare il testo, utilizzerai il TextLoader di LangChain.

import requests
from langchain.document_loaders import TextLoader

url = "https://raw.githubusercontent.com/langchain-ai/langchain/master/docs/docs/modules/state_of_the_union.txt"
res = requests.get(url)
with open("state_of_the_union.txt", "w") as f:
    f.write(res.text)

loader = TextLoader('./state_of_the_union.txt')
documents = loader.load()

Successivamente, dividi i tuoi documenti in segmenti – Poiché il Documento, nello stato originale, è troppo lungo per essere inserito nella finestra di contesto di LLM, è necessario suddividerlo in pezzi più piccoli. LangChain dispone di molti divisori di testo integrati per questo scopo. Per questo semplice esempio, puoi utilizzare il CharacterTextSplitter con una chunk_size di circa 500 e una sovrapposizione di chunk di 50 per preservare la continuità del testo tra i pezzi.

from langchain.text_splitter import CharacterTextSplitter

text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(documents)

Infine, incorpora e memorizza i pezzi – Per consentire la ricerca semantica tra i pezzi di testo, è necessario generare le rappresentazioni vettoriali per ogni pezzo e quindi memorizzarle insieme alle loro rappresentazioni vettoriali. Per generare le rappresentazioni vettoriali, puoi utilizzare il modello di embedding di OpenAI e per memorizzarle puoi utilizzare il database di vettori Weaviate. Chiamando .from_documents(), il database di vettori viene automaticamente popolato con i pezzi.

from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Weaviate
import weaviate
from weaviate.embedded import EmbeddedOptions

client = weaviate.Client(embedded_options=EmbeddedOptions())
vectorstore = Weaviate.from_documents(
    client=client,
    documents=chunks,
    embedding=OpenAIEmbeddings(),
    by_text=False)

Passo 1: Recupero

Una volta che il database di vettori è popolato, puoi definirlo come il componente di recupero, che recupera il contesto aggiuntivo in base alla similarità semantica tra la query dell’utente e i pezzi incorporati.

retriever = vectorstore.as_retriever()

Passo 2: Arricchimento

Successivamente, per arricchire l’input con il contesto aggiuntivo, è necessario preparare un modello di prompt. Il prompt può essere facilmente personalizzato da un modello di prompt, come mostrato di seguito.

from langchain.prompts import ChatPromptTemplate

template = """Sei un assistente per compiti di domande e risposte. Utilizza i seguenti pezzi di contesto recuperato per rispondere alla domanda. Se non conosci la risposta, semplicemente di' che non lo sai. Utilizza al massimo tre frasi e mantieni la risposta concisa.
Domanda: {question}
Contesto: {context}
Risposta:"""

prompt = ChatPromptTemplate.from_template(template)
print(prompt)

Passo 3: Generazione

Infine, puoi creare una catena per la pipeline RAG, concatenando il recupero, il modello di prompt e LLM. Una volta definita la catena RAG, puoi invocarla.

from langchain.chat_models import ChatOpenAI
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser

llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
rag_chain = (
    {"context": retriever,  "question": RunnablePassthrough()}
     | prompt
     | llm
    | StrOutputParser()
)

query = "Cosa ha detto il presidente riguardo alla giustizia Breyer"
rag_chain.invoke(query)

"Il presidente ha ringraziato il giudice Breyer per il suo servizio e ha riconosciuto il suo impegno nel servire il paese. Il presidente ha anche menzionato di aver nominato il giudice Ketanji Brown Jackson come suo successore per continuare l'eccellenza della giustizia Breyer."

Puoi vedere il risultato del flusso di lavoro RAG per questo specifico esempio illustrato di seguito:

Flusso di lavoro dell'elaborazione assistita dal recupero

Sommario

Questo articolo ha trattato il concetto di RAG, presentato nel paper Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks [1] del 2020. Dopo aver esaminato alcune teorie dietro il concetto, inclusa la motivazione e la soluzione del problema, questo articolo ha convertito la sua implementazione in Python. Questo articolo ha implementato un flusso di lavoro RAG utilizzando un OpenAI LLM in combinazione con un database vettoriale Weaviate e un modello di embedding OpenAI. LangChain è stato utilizzato per l’orchestrazione.

Ti è piaciuta questa storia?

Iscriviti gratuitamente per essere informato quando pubblico una nuova storia.

Ricevi una email ogni volta che Leonie Monigatti pubblica.

Ricevi una email ogni volta che Leonie Monigatti pubblica. Iscrivendoti, creerai un account VoAGI se non ne hai già uno…

VoAGI.com

Trova me su LinkedIn, Twitter, e Kaggle!

Avviso legale

Sono un Sostenitore dello sviluppatore presso Weaviate al momento della stesura di questo articolo. Oltre a questo articolo, ho aggiunto lo stesso esempio nel quaderno Weaviate nella documentazione LangChain. In alternativa, puoi iniziare seguendo il rag-weaviate modello in LangChain.

Riferimenti

Letteratura

[1] Lewis, P., et al. (2020). Retrieval-augmented generation for knowledge-intensive NLP tasks. Advances in Neural Information Processing Systems, 33, 9459–9474.

Immagini

Se non diversamente specificato, tutte le immagini sono create dall’autore.