Interroga i tuoi DataFrames con potenti modelli di linguaggio di grandi dimensioni utilizzando LangChain.

Interroga i tuoi DataFrames con modelli di linguaggio potenti e dimensioni elevate usando LangChain.

Preparati ad utilizzare i tuoi dati con i Large Language Models di Hugging Face utilizzando un database di vettori e LangChain!

Immagine dell'autore, generata con Dall-e 2

Nell’articolo precedente, ho spiegato come utilizzare un database di vettori come ChromaDB per archiviare informazioni e utilizzarle per creare una richiesta potenziata per interrogare i Large Language Models di Hugging Face.

In questo articolo, vedremo come utilizzare LangChain per lo stesso compito. LangChain si occuperà di cercare attraverso le informazioni archiviate in ChromaDB e di passarle direttamente al modello di linguaggio in uso.

In questo modo, possiamo utilizzare i nostri dati con i Large Language Models senza la necessità di eseguire un addestramento specifico del modello.

Dato che utilizzeremo modelli di Hugging Face, che possono essere scaricati e ospitati sui nostri server o spazi cloud privati, le informazioni non dovranno passare attraverso aziende come OpenAI.

Descriviamo i passaggi che seguiremo in questo articolo:

  1. Installare le librerie necessarie, come ChromaDB o LangChain
  2. Caricare il dataset e creare un documento in LangChain utilizzando uno dei suoi caricatori di documenti.
  3. Generare gli embedding da archiviare nel database.
  4. Creare un indice con le informazioni.
  5. Configurare un retriever con l’indice, che LangChain utilizzerà per recuperare le informazioni.
  6. Caricare il modello di Hugging Face.
  7. Creare una pipeline di LangChain utilizzando il modello di linguaggio e il retriever.
  8. Utilizzare la pipeline per fare domande.

Quali tecnologie stiamo utilizzando?

Il database di vettori che utilizzeremo è ChromaDB. È probabilmente la scelta più conosciuta tra i database di vettori open-source.

Per i modelli, abbiamo scelto due modelli nella libreria di Hugging Face. Il primo è dolly-v2–3b e l’altro flan-t5-large. È importante notare che questi non sono solo due modelli diversi, ma sono anche addestrati per diverse funzioni.

T5 è una famiglia di modelli text2text-generation, progettati per generare testo basato sui dataset su cui sono stati addestrati. Possono essere utilizzati per la generazione di testo, ma le loro risposte potrebbero non essere molto creative.

D’altra parte, Dolly è una famiglia di modelli di text-generation pura. Questi modelli tendono a produrre risposte più creative ed estese.

La libreria principale è LangChain, una piattaforma open-source che consente la creazione di applicazioni di linguaggio naturale sfruttando la potenza dei large language models. Ci permette di concatenare input e output tra questi modelli e altre librerie o prodotti, come database o vari plugin.

Iniziamo il progetto con LangChain.

Il codice è disponibile in un notebook su Kaggle. Questo articolo e il notebook fanno parte di un corso sulla creazione di applicazioni con large language models, che è disponibile sul mio profilo GitHub.

Se non vuoi perderti lezioni o aggiornamenti sui contenuti esistenti, è meglio seguire il repository. Pubblicherò nuove lezioni nel repository pubblico man mano che le completerò.

Chiedi i tuoi documenti con LangChain, VectorDB & HF

Esplora ed esegui codice di machine learning con Kaggle Notebooks | Utilizzando dati da diverse fonti di dati

www.kaggle.com

GitHub – peremartra/Large-Language-Model-Notebooks-Course

Contribuisci allo sviluppo di peremartra/Large-Language-Model-Notebooks-Course creando un account su GitHub.

github.com

Installazione e caricamento delle librerie.

Se stai lavorando nel tuo ambiente personale e hai già testato queste tecnologie, potresti non aver bisogno di installare nulla. Tuttavia, se stai utilizzando Kaggle o Colab, dovrai installare le seguenti librerie:

  • langchain: La libreria rivoluzionaria che consente la creazione di applicazioni con grandi modelli di linguaggio.
  • sentence_transformers: Dovremo generare le rappresentazioni dei testi che vogliamo memorizzare nel database vettoriale, per cui è necessaria questa libreria.
  • chromadb: Il database vettoriale da utilizzare. In particolare, ChromaDB si distingue per la sua interfaccia user-friendly.
!pip install chromadb!pip install langchain!pip install sentence_transformers

Oltre a queste librerie, importeremo anche le due librerie Python più utilizzate nella scienza dei dati: pandas e numpy.

import numpy as np import pandas as pd

Caricamento dei dataset.

Come ho già detto, il notebook è stato preparato per lavorare con due dataset differenti. Questi dataset sono gli stessi utilizzati nell’esempio precedente di RAG (Retrieval Augmented Generation).

RAG significa utilizzare i tuoi dati con grandi modelli di linguaggio, comunemente definiti come ‘Interrogare i tuoi documenti’.

Entrambi i dataset sono tabulari e contengono informazioni relative alle notizie:

  1. Dataset di notizie con etichetta dell’argomento.
  2. Notizie AI MIT pubblicate fino al 2023.

Il contenuto di entrambi i dataset è simile, ma i nomi delle colonne e le informazioni memorizzate differiscono. Personalmente, ritengo che utilizzare più dataset possa fornire un buon valore, consentendoti di convalidare e generalizzare i risultati con diverse fonti di dati.

Sarebbe utile esplorare un terzo dataset differente e replicare la funzionalità con esso al termine del notebook.

Dato che stiamo lavorando con risorse limitate su Kaggle, è fondamentale tenere conto dei vincoli di memoria. Pertanto, non lavoreremo con l’intero dataset per evitare di superare il limite di memoria di Kaggle, che è di 30 GB se non si utilizzano le GPU.

Lavorare con un sottoinsieme più piccolo del dataset ci permetterà comunque di esplorare e dimostrare efficacemente la funzionalità di LangChain, rimanendo entro i limiti delle risorse.

Diamo un’occhiata ai primi due record del topic-labeled-news-dataset.

Nel primo dataset, utilizzeremo la colonna title come nostro documento. Anche se i testi potrebbero non essere molto lunghi, ci serve come esempio perfetto. Possiamo usarlo per cercare nel database degli articoli e trovare quelli che discutono di un argomento specifico.

news = pd.read_csv('/kaggle/input/topic-labeled-news-dataset/labelled_newscatcher_dataset.csv', sep=';')MAX_NEWS = 1000DOCUMENT="title"TOPIC="topic"#news = pd.read_csv('/kaggle/input/bbc-news/bbc_news.csv')#MAX_NEWS = 500#DOCUMENT="description"#TOPIC="title"#Perché si tratta solo di un corso selezioniamo una piccola porzione di Notizie.subset_news = news.head(MAX_NEWS)

Abbiamo creato il DataFrame subset_news che contiene una porzione delle notizie dal dataset.

Per utilizzare uno dei due dataset, è sufficiente rimuovere i commenti dalle linee corrispondenti al dataset che vogliamo utilizzare. In ogni caso, abbiamo adattato il nome della colonna da utilizzare come dati e il numero di record che il sottoinsieme conterrà. Questo approccio ci consente di passare facilmente tra i dataset.

Genera il documento dal DataFrame.

Per creare il documento, useremo LangChain, utilizzando uno dei suoi loader. Per il nostro esempio, utilizzeremo il DataFrameLoader, ma ci sono vari loader disponibili per una vasta gamma di fonti, come CSV, file di testo, HTML, JSON, PDF e persino loader per prodotti come Confluence.

from langchain.document_loaders import DataFrameLoaderfrom langchain.vectorstores import Chroma

Una volta caricata la libreria, dobbiamo creare il loader. Per fare ciò, specificare il DataFrame e il nome della colonna che vogliamo utilizzare come contenuto del documento. Queste informazioni verranno passate al database vettoriale, ChromaDB, dove verranno memorizzate e utilizzate dal modello di linguaggio quando genera le sue risposte.

df_loader = DataFrameLoader(subset_news, page_content_column=DOCUMENT)

Per creare il documento, è sufficiente chiamare la funzione load del Loader.

df_document = df_loader.load()display(df_document)

Diamo un’occhiata al contenuto del documento:

[Documento(page_content="Uno sguardo più attento al potenziale di carburante solare della scissione dell'acqua", metadata={'topic': 'SCIENZA', 'link': 'https://www.eurekalert.org/pub_releases/2020-08/dbnl-acl080620.php', 'dominio': 'eurekalert.org', 'data_pubblicazione': '2020-08-06 13:59:45', 'lingua': 'en'}), Documento(page_content='Uno studio scopre che un profumo irresistibile fa sciami di locuste', metadata={'topic': 'SCIENZA', 'link': 'https://www.pulse.ng/news/world/an-irresistible-scent-makes-locusts-swarm-study-finds/jy784jw', 'dominio': 'pulse.ng', 'data_pubblicazione': '2020-08-12 15:14:19', 'lingua': 'en'}),

Come possiamo vedere, è stato creato un documento in cui ogni pagina corrisponde al contenuto di un record della colonna specificata. Inoltre, troviamo gli altri dati nel campo ‘metadata’, etichettati con il nome della colonna.

Vi incoraggio a provare con l’altro dataset e vedere come appaiono i dati.

Creazione degli Embeddings.

Prima di tutto, nel caso sia necessario, cerchiamo di capire cos’è un Embedding. È semplicemente una rappresentazione numerica di qualsiasi dato. Nel nostro caso specifico, sarà la rappresentazione numerica del testo da memorizzare.

Questa rappresentazione numerica assume la forma di vettori. Un vettore è semplicemente una rappresentazione di un punto in uno spazio multidimensionale. In altre parole, non dobbiamo visualizzare il punto su un piano bidimensionale o tridimensionale, come siamo abituati a fare. Il vettore può rappresentare il punto in qualsiasi numero di dimensioni.

Per noi, può sembrare complicato o difficile da immaginare, ma matematicamente non c’è molta differenza nel calcolare la distanza tra due punti, che siano in due dimensioni, tre o qualsiasi numero di dimensioni.

Questi vettori ci permettono di calcolare le differenze o le somiglianze tra di loro, rendendo possibile cercare informazioni simili in modo molto efficiente.

Il trucco sta nel determinare quali vettori assegniamo a ciascuna parola, poiché vogliamo che le parole con significati simili siano più vicine in distanza rispetto a quelle con significati più diversi. Le librerie di Hugging Face si occupano di questo aspetto, quindi non dobbiamo preoccuparci troppo. Dobbiamo solo assicurarci una conversione coerente per tutti i dati da memorizzare e le query da eseguire.

Importiamo un paio di librerie:

  1. CharacterTextSplitter: Utilizzeremo questa libreria per raggruppare le informazioni in blocchi.
  2. HuggingFaceEmbeddings o SentenceTransformerEmbedding: Nel notebook, ho usato entrambi e non ho riscontrato alcuna differenza tra loro. Queste librerie sono responsabili di recuperare il modello che eseguirà l’embedding dei dati.
from langchain.text_splitter import <a></a><a>CharacterTextSplitter#from langchain.embeddings import HuggingFaceEmbeddings

Non c’è un modo corretto al 100% per dividere i documenti in blocchi. La considerazione chiave è che blocchi più grandi forniranno al modello più contesto. Tuttavia, l’utilizzo di blocchi più grandi aumenterà anche la dimensione del nostro Vector Store, che può richiedere molta memoria.

È essenziale trovare un equilibrio tra la dimensione del contesto e l’utilizzo della memoria per ottimizzare le prestazioni della nostra applicazione.

Ho deciso di utilizzare una dimensione del blocco di 250 caratteri con un overlap di 10. Ciò significa che gli ultimi 10 caratteri di un blocco saranno i primi 10 caratteri del blocco successivo. È una dimensione di blocco relativamente piccola, ma è più che sufficiente per il tipo di informazioni con cui stiamo lavorando.

text_splitter = CharacterTextSplitter(chunk_size=250, chunk_overlap=10)texts = text_splitter.split_documents(df_document)

Ora possiamo creare embeddings con il testo.

from langchain.embeddings.sentence_transformer import SentenceTransformerEmbeddingsembedding_function = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")#embedding_function = HuggingFaceEmbeddings(#    model_name="sentence-transformers/all-MiniLM-L6-v2"#)  

Come puoi vedere, ho usato SentenceTransformerEmbeddings invece di HuggingFaceEmbeddings. Puoi facilmente cambiarlo modificando la riga commentata.

Con entrambe le librerie, puoi chiamare lo stesso modello pre-addestrato per generare embeddings. Sto usando all-MiniLM-L6-v2 per entrambi. Pertanto, sebbene possano esserci lievi differenze tra gli embeddings generati da ciascuna libreria, saranno minime e non influenzeranno significativamente le prestazioni.

Inizialmente, SentenceTransformerEmbeddings è specializzato nella trasformazione di frasi, mentre HuggingFaceEmbeddings è più generale, in grado di generare embeddings per paragrafi o interi documenti.

In effetti, data la natura dei nostri documenti, ci si aspetta che non ci sia differenza nell’utilizzo di una delle due librerie.

Con gli embeddings generati, possiamo creare l’indice

chromadb_index = Chroma.from_documents(    texts, embedding_function, persist_directory='./input')

Questo indice è ciò che utilizzeremo per fare domande ed è appositamente progettato per essere altamente efficiente! Dopo tutti questi sforzi, l’ultima cosa che vorremmo è che sia lento e impreciso :-).

Iniziamo ad usare LangChain!

Ora arriva la parte divertente: concatenare le azioni con LangChain per creare la nostra prima piccola applicazione utilizzando un grande modello di linguaggio!

L’applicazione è molto semplice, composta solo da due passaggi e due componenti. Il primo passaggio coinvolgerà un retriever. Questo componente viene utilizzato per recuperare informazioni dai documenti o dal testo che forniamo come documento. Nel nostro caso, effettuerà una ricerca basata sulla similarità utilizzando gli embeddings per recuperare informazioni rilevanti per la query dell’utente da ciò che abbiamo memorizzato in ChromaDB.

Il secondo e ultimo passaggio coinvolgerà il nostro modello di linguaggio, che riceverà le informazioni restituite dal retriever.

Pertanto, dobbiamo importare le librerie per creare il retriever e il pipeline.

from langchain.chains import RetrievalQAfrom langchain.llms import HuggingFacePipeline

Ora possiamo creare il retriever, utilizzando l’indice degli embeddings che abbiamo creato in precedenza.

retriever = chromadb_index.as_retriever()

Abbiamo completato il primo passaggio della nostra catena, o pipeline. Ora, passiamo al secondo: il modello di linguaggio.

Nel notebook, ho utilizzato due modelli diversi disponibili in Hugging Face.

Il primo modello è dolly-v2–3b, il più piccolo della famiglia Dolly. Personalmente mi piace molto questo modello. Sebbene potrebbe non essere così popolare come altri, le risposte che genera sono significativamente migliori rispetto a quelle di GPT-2, raggiungendo un livello simile a quello che potremmo ottenere con GPT-3.5 di OpenAI. Con 3 miliardi di parametri, si avvicina al limite di memoria che possiamo caricare su Kaggle. Questo modello è addestrato per la generazione di testo, il che si traduce in risposte ben strutturate.

Il secondo modello fa parte della famiglia T5. Presta attenzione perché questo modello è specificamente progettato per la generazione di testo-2-testo, risultando in risposte molto più concise e brevi.

Assicurati di provare entrambi i modelli almeno per vedere come si comportano.

model_id = "databricks/dolly-v2-3b" #il mio modello preferito per la generazione di testi per il testtask="text-generation"#model_id = "google/flan-t5-large" #Bel modello di testo2testo#task="text2text-generation"

Perfetto! Abbiamo tutto quello che ci serve per creare il pipeline! Ora facciamolo!

hf_llm = HuggingFacePipeline.from_model_id(    model_id=model_id,    task=task,    model_kwargs={        "temperature": 0,        "max_length": 256    },)

Vediamo cosa significa ciascuno dei parametri:

  1. model_id: L’identificatore del modello in Hugging Face. Puoi ottenerlo da Hugging Face e di solito consiste nel nome del modello seguito dalla versione.
  2. task: Qui specifichiamo il compito per cui vogliamo utilizzare il modello. Alcuni modelli sono addestrati per compiti multipli. Puoi trovare i compiti supportati per un modello specifico nella documentazione di Hugging Face del modello.
  3. model_kwargs: Questo parametro ci consente di specificare argomenti aggiuntivi specifici del modello. In questo caso, sto fornendo la temperatura (quanto creativo vogliamo che sia il modello) e la lunghezza massima della risposta.

Ora è il momento di configurare la pipeline utilizzando il modello e il retriever.

document_qa = RetrievalQA.from_chain_type(    llm=hf_llm, chain_type="stuff", retriever=retriever)

Nella variabile chain_type, indichiamo come dovrebbe funzionare la catena e abbiamo quattro opzioni:

  1. stuff: La opzione più semplice, prende solo i documenti ritenuti appropriati e li utilizza nel prompt da passare al modello.
  2. refine: Effettua più chiamate al modello con documenti diversi, cercando di ottenere una risposta più raffinata ogni volta. Può eseguire un elevato numero di chiamate al modello, quindi va usato con cautela.
  3. map reduce: Cerca di ridurre tutti i documenti in uno solo, eventualmente attraverso più iterazioni. Può comprimere e unire i documenti per adattarli al prompt inviato al modello.
  4. map re-rank: Chiama il modello per ogni documento e li classifica, restituendo infine il migliore. Simile a `refine`, può essere rischioso a seconda del numero di chiamate previste.

Ora possiamo utilizzare la catena appena creata per fare domande e queste verranno risposte considerando i dati del nostro DataFrame, che ora fa parte del Vector Database.

#Domanda di esempio per il dataset newscatcher. response = document_qa.run("Posso comprare un laptop Toshiba?")#Domanda di esempio per il dataset BBC. #response = document_qa.run("Chi incontrerà Boris Johnson?")display(response)

Quindi, come è evidente, la risposta ottenuta dipenderà dal dataset utilizzato e anche dal modello. Per la domanda se possiamo comprare un laptop Toshiba, otteniamo due risposte molto diverse a seconda del modello:

Dolly: “No, Toshiba chiude ufficialmente i loro laptop nel 2023. Il leggendario Toshiba ha ufficialmente smesso di produrre laptop. Toshiba chiude ufficialmente i loro laptop nel 2023.”

T5: “No.”

Come puoi vedere, ogni modello aggiunge la propria personalità alla risposta!

Conclusioni e Continuazione dell’Apprendimento!

In realtà è stato molto più semplice di quanto si possa pensare. È molto più facile ora rispetto a prima dell’esplosione dei grandi modelli di linguaggio e all’emergere di strumenti come LangChain.

Abbiamo utilizzato un Vector Database per memorizzare i dati che abbiamo precedentemente caricato in un DataFrame. Anche se avremmo potuto utilizzare qualsiasi altra fonte di dati.

Li abbiamo utilizzati come input per un paio di modelli di linguaggio disponibili in Hugging Face e abbiamo osservato come i modelli forniscano una risposta considerando le informazioni dal DataFrame.

Ma non fermarti qui, apporta le tue modifiche personali al notebook e risolvi eventuali problemi che possono sorgere. Alcune idee includono:

  • Utilizzare entrambi i dataset, e preferibilmente cercarne un terzo. Ancora meglio, pensi di poterlo adattare per leggere il tuo curriculum? Sono sicuro che si possa ottenere con qualche piccola modifica.
  • Prova a utilizzare un terzo modello di Hugging Face.
  • Cambia la fonte dei dati. Potrebbe essere un file di testo, un file Excel o anche uno strumento di documenti come Confluence.

L’intero corso sui grandi modelli di linguaggio è disponibile su Github. Per rimanere aggiornato su nuovi articoli, considera di seguire il repository o di dargli una stella. In questo modo, riceverai notifiche ogni volta che verrà aggiunto nuovo contenuto.

GitHub – peremartra/Large-Language-Model-Notebooks-Course

Contribuisci allo sviluppo di peremartra/Large-Language-Model-Notebooks-Course creando un account su GitHub.

github.com

Questo articolo fa parte di una serie in cui esploriamo le applicazioni pratiche dei grandi modelli di linguaggio. Puoi trovare il resto degli articoli nella seguente lista:

Pere Martra

Corso Pratico sui Grandi Modelli di Linguaggio

Visualizza elenco3 storie

Scrivo regolarmente su Deep Learning e machine learning. Considera seguirmi su VoAGI per ricevere aggiornamenti su nuovi articoli. E, naturalmente, sei il benvenuto a connetterti con me su LinkedIn.