Mitigare le allucinazioni attraverso la generazione potenziata dal recupero utilizzando il database vettoriale di Pinecone e Llama-2 da Amazon SageMaker JumpStart

Affrontare le allucinazioni con la potente generazione potenziata dal recupero il database vettoriale di Pinecone e Llama-2 di Amazon SageMaker JumpStart

Nonostante l’adozione apparentemente inarrestabile di LLM in tutti i settori, sono solo uno dei componenti di un ecosistema tecnologico più ampio che alimenta la nuova ondata di intelligenza artificiale. Molti casi d’uso di intelligenza artificiale conversazionale richiedono LLM come Llama 2, Flan T5 e Bloom per rispondere alle domande degli utenti. Questi modelli si basano sulla conoscenza parametrica per rispondere alle domande. Il modello apprende questa conoscenza durante l’addestramento e la codifica nei parametri del modello. Per aggiornare questa conoscenza, dobbiamo riallenare il LLM, il che richiede molto tempo e denaro.

Fortunatamente, possiamo anche utilizzare la conoscenza di origine per informare i nostri LLM. La conoscenza di origine è un’informazione alimentata nel LLM attraverso un prompt di input. Un approccio popolare per fornire conoscenza di origine è la Generazione con Recupero Integrato (RAG). Utilizzando RAG, recuperiamo informazioni rilevanti da una fonte di dati esterna e alimentiamo tali informazioni nel LLM.

In questo articolo del blog, esploreremo come distribuire LLM come Llama-2 utilizzando Amazon Sagemaker JumpStart e mantenere i nostri LLM aggiornati con informazioni rilevanti attraverso la Generazione con Recupero Integrato (RAG) utilizzando il database vettoriale Pinecone al fine di prevenire l’allucinazione dell’AI.

Generazione con Recupero Integrato (RAG) in Amazon SageMaker

Pinecone si occuperà del componente di recupero di RAG, ma hai bisogno di altri due componenti critici: un luogo per eseguire l’infere

Oppure, SageMaker ha un insieme di modelli direttamente compatibili con un oggetto JumpStartModel più semplice. Molti LLM popolari come Llama 2 sono supportati da questo modello, che può essere inizializzato come mostrato nella seguente cattura dello schermo:

import sagemaker from sagemaker.jumpstart.model import JumpStartModel role = sagemaker.get_execution_role() my_model = JumpStartModel(model_id = "meta-textgeneration-llama-2-7b-f")

Per entrambe le versioni di my_model, distribuirle come mostrato nella seguente cattura dello schermo:

predictor = my_model.deploy(    initial_instance_count=1, instance_type="ml.g5.4xlarge", endpoint_name="llama-2-generator")

Interrogare l’LLM pre-addestrato

Con il nostro endpoint LLM inizializzato, puoi iniziare a fare query. Il formato delle nostre query può variare (in particolare tra LLM conversazionali e non conversazionali), ma il processo è generalmente lo stesso. Per il modello Hugging Face, seguire i seguenti passaggi:

# https://aws.amazon.com/blogs/machine-learning/llama-2-foundation-models-from-meta-are-now-available-in-amazon-sagemaker-jumpstart/prompt = """Rispondi alla SEGUENTE DOMANDA basandoti sul CONTESTO fornito. Se non conosci la risposta e il CONTESTO non contiene la risposta, dì sinceramente "Non soRISPOSTA:"""payload = {    "inputs":        [        [         {"role": "system", "content": prompt},         {"role": "user", "content": question},        ]         ],   "parameters":{"max_new_tokens": 64, "top_p": 0.9, "temperature": 0.6, "return_full_text": False}}out = predictor.predict(payload, custom_attributes='accept_eula=true')out[0]['generation']['content']

Puoi trovare la soluzione nel repository GitHub.

La risposta generata che riceviamo qui non ha molto senso, è un’allucinazione.

Fornire un Contesto Addizionale all’LLM

Llama 2 cerca di rispondere alla nostra domanda basandosi esclusivamente sulla conoscenza parametrica interna. Chiaramente, i parametri del modello non conservano la conoscenza di quali istanze possiamo utilizzare con il training spot gestito in SageMaker.

Per rispondere correttamente a questa domanda, dobbiamo utilizzare la conoscenza di origine. Ciò significa che forniamo ulteriori informazioni all’LLM tramite il prompt. Aggiungiamo tali informazioni direttamente come contesto aggiuntivo per il modello.

context = """Il training spot gestito può essere utilizzato con tutte le istanze supportate in Amazon SageMaker. Il training spot gestito è supportato in tutte le regioni AWS in cui Amazon SageMaker è attualmente disponibile."""prompt_template = """Rispondi alla SEGUENTE DOMANDA basandoti sul CONTESTO fornito. Se non conosci la risposta e il CONTESTO non contiene la risposta, dì sinceramente "Non so".CONTESTO:{context}RISPOSTA:"""text_input = prompt_template.replace("{context}", context).replace("{question}", question)payload = {    "inputs":        [        [         {"role": "system", "content": text_input},         {"role": "user", "content": question},        ]         ],   "parameters":{"max_new_tokens": 64, "top_p": 0.9, "temperature": 0.6, "return_full_text": False}}out = predictor.predict(payload, custom_attributes='accept_eula=true')generated_text = out[0]['generation']['content']print(f"[Input]: {question}\n[Output]: {generated_text}")[Input]: Quali istanze posso utilizzare con il training spot gestito in SageMaker?[Output]: Basandosi sul contesto fornito, puoi utilizzare il training spot gestito con tutte le istanze supportate in Amazon SageMaker. Quindi, la risposta è: tutte le istanze supportate in Amazon SageMaker.

Ora vediamo la risposta corretta alla domanda; è stato facile! Tuttavia, un utente difficilmente inserirebbe contesti nelle loro domande, conoscerebbero già la risposta alla loro domanda.

Piuttosto che inserire manualmente un singolo contesto, identifica automaticamente le informazioni rilevanti da un database più esteso di informazioni. Per questo, avrai bisogno di Retrieval Augmented Generation.

Generazione potenziata del recupero

Con la generazione potenziata del recupero, puoi codificare un database di informazioni in uno spazio vettoriale in cui la prossimità tra i vettori rappresenta la loro rilevanza/similarità semantica. Con questo spazio vettoriale come base di conoscenza, puoi convertire una nuova interrogazione dell’utente, codificarla nello stesso spazio vettoriale e recuperare i record più rilevanti precedentemente indicizzati.

Dopo aver recuperato questi record rilevanti, selezionane alcuni e includili nel prompt del LLM come contesto aggiuntivo, fornendo al LLM una conoscenza di origine altamente rilevante. Si tratta di un processo in due fasi in cui:

  • L’indicizzazione popola l’indice vettoriale con informazioni da un set di dati.
  • Il recupero avviene durante una query ed è il punto in cui recuperiamo informazioni rilevanti dall’indice vettoriale.

Entrambe le fasi richiedono un modello di incorporamento per tradurre il nostro testo in linguaggio naturale in uno spazio vettoriale semantico. Utilizza il modello di trasformazione delle frasi MiniLM altamente efficiente di Hugging Face, come mostrato nella schermata seguente. Questo modello non è un LLM e quindi non viene inizializzato nello stesso modo del nostro modello Llama 2.

hub_config = {    "HF_MODEL_ID": "sentence-transformers/all-MiniLM-L6-v2",  # model_id da hf.co/models    "HF_TASK": "feature-extraction",}huggingface_model = HuggingFaceModel(    env=hub_config,    role=role,    transformers_version="4.6",  # versione di transformers utilizzata    pytorch_version="1.7",  # versione di PyTorch utilizzata    py_version="py36",  # versione di Python del DLC)

Nel hub_config, specifica l’ID del modello come mostrato nella schermata precedente, ma per il compito utilizza “feature-extraction” perché stiamo generando embedding vettoriali e non testo come nel caso del nostro LLM. Successivamente, inizializza la configurazione del modello con HuggingFaceModel come prima, ma questa volta senza l’immagine del LLM e con alcuni parametri di versione.

encoder = huggingface_model.deploy(    initial_instance_count=1, instance_type="ml.t2.large", endpoint_name="minilm-embedding")

Puoi distribuire nuovamente il modello con deploy, utilizzando l’istanza più piccola (solo CPU) di ml.t2.large. Il modello MiniLM è piccolo, quindi non richiede molta memoria e non ha bisogno di una GPU poiché può creare rapidamente degli embedding anche su una CPU. Se preferisci, puoi eseguire il modello più velocemente su una GPU.

Per creare degli embedding, utilizza il metodo predict e passa una lista di contesti da codificare tramite la chiave inputs come mostrato di seguito:

out = encoder.predict({"inputs": ["qui un po' di testo", "qui va un altro po' di testo"]})

Vengono passati due contesti di input, restituendo due embedding vettoriali di contesto come mostrato:

len(out)

2

La dimensionalità degli embedding del modello MiniLM è 384, il che significa che ogni embedding vettoriale prodotto da MiniLM dovrebbe avere una dimensionalità di 384. Tuttavia, osservando la lunghezza dei nostri embedding, si può vedere quanto segue:

len(out[0]), len(out[1])

(8, 8)

Due liste contengono ognuna otto elementi. MiniLM elabora prima il testo in una fase di tokenizzazione. Questa trasforma il nostro testo in linguaggio naturale in una lista di ID di token leggibili dal modello. Nelle caratteristiche di output del modello, puoi vedere gli embedding a livello di token. Uno di questi embedding mostra la dimensionalità attesa di 384 come mostrato:

len(out[0][0])

384

Trasforma questi embedding a livello di token in embedding a livello di documento utilizzando i valori medi lungo ciascuna dimensione vettoriale, come mostrato nell’illustrazione seguente.

Operazione di media pooling per ottenere un singolo vettore di dimensionalità 384.

import numpy as np embeddings = np.mean(np.array(out), axis=1)embeddings.shape(2, 384)

Con due embedding vettoriali di dimensione 384, uno per ogni testo di input. Per semplificarci la vita, racchiudiamo il processo di codifica in una singola funzione come mostrato nella seguente schermata:

from typing import Listdef embed_docs(docs: List[str]) -> List[List[float]]:    out = encoder.predict({"inputs": docs})    embeddings = np.mean(np.array(out), axis=1)    return embeddings.tolist()

Scaricare il dataset

Scarica le domande frequenti su Amazon SageMaker come base di conoscenza per ottenere i dati che contengono sia le colonne delle domande che delle risposte.

Scarica le domande frequenti su Amazon SageMaker

Quando esegui la ricerca, cerca solo le risposte, in modo da poter eliminare la colonna delle domande. Vedi il notebook per i dettagli.

Il nostro dataset e il processo di embedding sono pronti. Ora ci serve solo un posto dove archiviare tali embedding.

Indicizzazione

Il database vettoriale di Pinecone archivia gli embedding vettoriali e li ricerca in modo efficiente su larga scala. Per creare un database, avrai bisogno di una chiave API gratuita da Pinecone.

import pineconeimport os# aggiungi la chiave API di Pinecone da app.pinecone.ioapi_key = os.environ.get("PINECONE_API_KEY") or "YOUR_API_KEY"# imposta l'ambiente Pinecone - trova accanto alla chiave API nella consoleenv = os.environ.get("PINECONE_ENVIRONMENT") or "YOUR_ENV"pinecone.init(api_key=api_key, environment=env)

Dopo esserti connesso al database vettoriale di Pinecone, crea un singolo indice vettoriale (simile a una tabella nei database tradizionali). Dai all’indice il nome retrieval-augmentation-aws e allinea i parametri dimension e metric con quelli richiesti dal modello di embedding (MiniLM in questo caso).

import timeindex_name = "retrieval-augmentation-aws"if index_name in pinecone.list_indexes():    pinecone.delete_index(index_name)pinecone.create_index(name=index_name, dimension=embeddings.shape[1], metric="cosine")# attendi che l'indice finisca l'inizializzazionewhile not pinecone.describe_index(index_name).status["ready"]:    time.sleep(1)

Per iniziare a inserire i dati, esegui quanto segue:

from tqdm.auto import tqdmbatch_size = 2  # può essere aumentato ma richiede una dimensione di istanza più grande altrimenti l'istanza si esaurisce la memoriamvector_limit = 1000answers = df_knowledge[:vector_limit]index = pinecone.Index(index_name)for i in tqdm(range(0, len(answers), batch_size)):    # trova la fine del batch    i_end = min(i + batch_size, len(answers))    # crea un batch di ID    ids = [str(x) for x in range(i, i_end)]    # crea un batch di metadati    metadatas = [{"text": text} for text in answers["Answer"][i:i_end]]    # crea gli embedding    texts = answers["Answer"][i:i_end].tolist()    embeddings = embed_docs(texts)    # crea una lista di record per l'inserimento o l'aggiornamento    records = zip(ids, embeddings, metadatas)    # inserisci o aggiorna su Pinecone    index.upsert(vectors=records)

Puoi iniziare a interrogare l’indice con la domanda precedente in questo post.

# estrai gli embedding per le domandequery_vec = embed_docs(question)[0]# interroga Pineconeres = index.query(query_vec, top_k=1, include_metadata=True)# mostra i risultatires{'matches': [{'id': '90','metadata': {'text': 'Managed Spot Training can be used with all ''instances supported in Amazon ''SageMaker.\r\n'},'score': 0.881181657,'values': []}],'namespace': ''}

L’output sopra mostra che stiamo restituendo contesti rilevanti per aiutarci a rispondere alla nostra domanda. Poiché top_k = 1, index.query ha restituito il risultato migliore insieme ai metadati che leggono “Managed Spot Training can be used with all instances supported in Amazon”.

Aumentare il Prompt

Utilizza i contesti recuperati per aumentare il prompt e decidere una quantità massima di contesto da inserire nell’LLM. Utilizza il limite di 1000 caratteri per aggiungere iterative ogni contesto restituito al prompt fino a superare la lunghezza del contenuto.

Aumentare il Prompt

Aumentare il Prompt

Inserisci il valore di context_str nel prompt LLM come mostrato nella seguente cattura schermo:

payload = create_payload(domanda, context_str)out = predictor.predict(payload, custom_attributes='accept_eula=true')generated_text = out[0]['generation']['content']print(f"[Input]: {domanda}\n[Output]: {generated_text}")

[Input]: Quali istanze posso utilizzare con Managed Spot Training in SageMaker?[Output]:  Sulla base del contesto fornito, è possibile utilizzare Managed Spot Training con tutte le istanze supportate in Amazon SageMaker. Pertanto, la risposta è: tutte le istanze supportate in Amazon SageMaker.

La logica funziona, quindi racchiudila in una singola funzione per mantenere pulite le cose.

def rag_query(domanda: str) -> str:    # crea il vettore di query    query_vec = embed_docs(domanda)[0]    # esegui la query su Pinecone    res = index.query(query_vec, top_k=5, include_metadata=True)    # ottieni i contesti    contesti = [match.metadata["text"] for match in res.matches]    # costruisci la stringa di contesti multipli    context_str = construct_context(contexts=contesti)    # crea il nostro prompt aumentato di recupero    payload = create_payload(domanda, context_str)    # fai la previsione    out = predictor.predict(payload, custom_attributes='accept_eula=true')    return out[0]["generation"]["content"]

Ora puoi fare domande come quelle mostrate di seguito:

rag_query("SageMaker supporta le istanze spot?")' Sì, Amazon SageMaker supporta le istanze spot per il managed spot training. Sulla base del contesto fornito, il Managed Spot Training può essere utilizzato con tutte le istanze supportate in Amazon SageMaker, ed il Managed Spot Training è supportato in tutte le regioni AWS in cui Amazon SageMaker è attualmente disponibile.\n\nPertanto, la risposta alla tua domanda è:\n\nSì, SageMaker supporta le istanze spot in tutte le regioni in cui Amazon SageMaker è disponibile.'

Pulizia

Per evitare di incorrere in addebiti non desiderati, elimina il modello e il punto di arrivo.

encoder.delete_model()encoder.delete_endpoint()

Conclusione

In questo articolo, ti abbiamo presentato RAG con LLM open-access su SageMaker. Ti abbiamo anche mostrato come distribuire i modelli Amazon SageMaker Jumpstart con Llama 2, LLM Hugging Face con Flan T5 e modelli di embedding con MiniLM.

Abbiamo implementato un flusso di lavoro RAG completo end-to-end utilizzando i nostri modelli open-access e un indice vettoriale Pinecone. Utilizzando questo, abbiamo mostrato come ridurre al minimo le allucinazioni, mantenere aggiornate le conoscenze dell’LLM e, in definitiva, migliorare l’esperienza utente e la fiducia nei nostri sistemi.

Per eseguire questo esempio autonomamente, clona questo repository GitHub e segui i passaggi precedenti utilizzando il Notebook Question Answering su GitHub.