向量資料庫與LLM的整合:實踐指南

charlieroro發表於2024-03-15

向量資料庫與LLM的整合:實踐指南

本文將瞭解到什麼是向量資料庫,以及如何與LLMs進行整合。透過LLMs和向量資料庫的結合,可以節省微調帶來的開銷和時間。

通常,LLM會在各種各樣的資料上進行訓練,這使它們具有廣泛的理解能力,但可能會導致在特定的知識領域存在差距。有時,它們甚至可能產生與目標無關或帶有偏見的資訊——這是從廣闊但未經篩選的web學習的副產品。為了解決該問題,我們引入了向量資料庫(Vector Database)的概念。這些資料庫以一種稱為"向量嵌入"的獨特格式儲存資料,可以讓LLMs掌握和使用的資訊更連貫和準確。

本文給出瞭如何使用向量資料庫構建一個LLM,並改進LLM對該流程的使用。我們將會看到二者的結合是如何讓LLMs更加準確可靠(特別針對特定主題)。

下面我們簡單總結了向量資料庫,解釋向量嵌入的概念以及它在增加AI和機器學習應用方面的角色。之後我們會展示這些資料庫和傳統資料庫的不同之處,以及為什麼他們更適合AI任務,特別是與非結構資料(如文字、圖片和複雜模式)打交道時。

之後,我們會探索使用該技術來構建一個封閉式QA機器人應用,該機器人使用了Falcon-7B模型和ChromaDB向量資料庫,證明了LLMs與正確的工具和技術相結合時的成效。

最後,你將清楚認識到如何使用LLMs和向量資料庫來建立新穎、上下文相關並且可靠的應用。不管你是AI愛好者還是經驗豐富的開發者,本指南將幫助你輕鬆自信地探索這個激動人心的領域。

向量資料庫概述

在深入瞭解向量資料庫之前,需要了解向量嵌入(vector embedding)的概念。向量嵌入是機器學習中用於將原始資料轉換為AI系統可以理解的數值格式必不可少的一種技術,涉及資料轉換,如將文字或圖片轉換為一系列數字,這些數字在高維度空間稱之為向量。高緯度資料指包括很多屬性或特徵的資料,每個資料代表一個不同的維度,這些維度可以幫助捕獲該資料的細微特徵。

建立向量嵌入的過程開始於資料輸入,可以是語句中的任意內容或是圖片的畫素等。大語言模型和其他AI演算法會分析這些資料並確定其關鍵特徵。例如,在文字資料中,可能涉及理解單詞的意義和它在語句中的上下文。嵌入模型會將這些特徵轉換為一個數值格式,向量中的每個數值代表資料的一個特徵,透過將這些特徵數值封裝到一起,就可以作為機器可以處理的輸入。

之所以說這些向量是高緯度的,是因為它們包含很多數值,每個數值對應資料中的某個(不同)特徵。這種高維度使得向量能夠捕捉複雜、詳細的資訊,使之成為強大的AI模型工具。模型使用這些嵌入向量來識別資料中的模式、關係和潛在結構。

向量資料庫針對向量嵌入的特性提供了最佳化儲存和查詢的能力。它們擅長提供高效搜尋、高效能、可擴充套件性,並透過比較和識別資料點之間的相似性,實現資料的快速檢索。

這些數值代表了複雜的高緯度資訊,使之有別於傳統的主要使用文字和數字儲存資料的系統。向量資料庫的主要能力是管理和查詢如圖片、影片和文字格式的資料,當這些資料轉換為向量格式後,特別適用於機器學習和AI應用。

在下面示例中,我們將一段文字轉換為詞向量,這一步是神經語言處理的基本步驟,可以讓我們量化和分析語言關係。例如,"puppy"的向量表達應該更接近"dog"的向量空間,而不是"house",反映了它們的語義接近性。這種方式還可以擴充到類似的關係中。"man"和"woman"的向量距離和方向類似於"king"和"queen"之間的距離和方向。下面展示了單詞向量不僅僅代表單詞,還可以在多維度向量空間中對它們的語義關係進行有意義的比較。

image

LLMs崛起之前的向量資料庫

向量資料庫用於處理向量嵌入,已經有一些關鍵使用場景,特別是在機器學習和AI領域:

相似搜尋:這是向量資料庫的關鍵功能。它們可以在高維度空間內找出與給定請求相似的資料點。特別適用於圖形和音訊檢索(希望找出和特定輸入類似的內容),下面是一些業界使用場景:

  • 電商:透過允許客戶搜尋與參考影像相似的產品來增強產品發現能力。
  • 音樂流服務:找出並給使用者推薦在音訊特徵上和喜歡的曲目類似的歌曲。
  • 醫療成像:透過檢索並對相似病理的醫學影像(如X光或MRI)進行比較分析來幫助放射科醫生。

推薦系統:向量資料庫透過處理使用者和商品嵌入來支援推薦系統。它可以將使用者和他們感興趣或過去有過互動的物品(如產品、電影或文章)關聯起來。下面是一個業務使用場景:

  • 流平臺:個性化觀看體驗,透過收看的瀏覽歷史來推薦電影和電視。
  • 線上零售:根據購買者的瀏覽和購買歷史來推薦產品,增強交叉銷售和追加銷售機會。
  • 新聞聚合:透過匹配讀者過去的參與模式和偏好來分發個性化新聞。

基於內容的檢索:這裡向量資料庫用於基於實際內容而非傳統後設資料來檢索內容。特別對於非結構化資料,如文字和影像,需要首先對內容本身進行分析。下面是一些業界使用場景:

  • 數字資產管理: 透過方便地根據視覺或音訊內容特徵搜尋和檢索影像或影片,使公司能夠管理龐大的數字媒體庫。
  • 法律和合規:通常查詢大量文件來查詢與法律案件或合規性調查上下文相關的資訊和文件。
  • 學術研究:幫助研究者查詢與他們工作上下文相關的學術文件和研究論文(即使不指定關鍵字)

關於基於內容的檢索的最後一點越來越重要,並促進了一種新的應用:

使用上下文理解來增強LLMs:透過儲存和處理文字嵌入,向量資料庫可以幫助LLMs實現更精細化以及上下文相關的檢索工作。它們可以幫助理解大量文字的語義內容,在回答複雜搜尋、維護對話上下文或生成相關內容等任務中起著關鍵作用。這種應用正迅速成為向量資料庫的一個重要場景,展示了其在增強高階AI系統(如LLM)能力方面起到的作用。

向量資料庫 vs 傳統資料庫

傳統的SQL資料庫在結構化資料管理方面表現出色,擅長處理精確匹配和明確定義的條件邏輯。它們維護資料的完整性,適合需要精確、結構化資料處理的應用程式。但這種剛性設計模式也使得它們不太適合處理非結構化資料的語義以及上下文之間的細微差別,而這正是LLM和生成式AI應用所需要的。

另一方面,相比傳統的SQL系統,NoSQL資料庫則更加靈活。它們可以處理半結構化和非結構化資料,如JSON文件,這使得它們更適用於AI和機器學習場景。但即便如此,相比用於LLMs和生成式的向量資料庫來說,NoSQL資料庫依然缺少某些方面的能力,如解析上下文、模式以及簡單資料檢索之外的語義內容。

向量資料庫彌補了這一空白。它們以AI為中心,使用向量的方式處理資料,可以有效管理複雜的非結構化資料。當與LLMs協作時,向量資料庫支援相似性查詢和上下文理解,提供了超出傳統SQL和NoSQL資料庫的能力。它們擅長處理近似值和模式識別,使之特別適用於對資料的微妙理解比精確資料匹配更重要的AI應用。

提升向量資料庫的效能

對於依賴速度和精確檢索高緯資料的應用來說,向量資料庫的效能最佳化至關重要。這裡面涉及提升查詢速度、保證高準確性以及有效處理不斷增長的資料量和使用者請求。其中最重要的一部分是圍繞索引策略進行最佳化(一種更有效組織和查詢向量資料的技術)。下面,我們將介紹這些索引策略,以及它們是如何提升向量資料庫效能的。

索引策略

向量資料庫的索引策略用於方便和準確檢索與查詢向量相似的向量。這些策略可以顯著影響到查詢操作的速度和準確性。

  • 量化(Quantization):量化涉及將向量對映到向量空間中的一組有限的參考點,從而有效地壓縮向量資料。這種策略透過將查詢限制到有限點(而非整個資料庫)的方式降低了儲存需求並加快查詢速度。量化的方式有很多種,如標量量化(Scalar Quantization,透過將大空間的向量轉換成相對小空間的向量來減少整體的儲存空間。 通常是將浮點數(比如f32)向量量化成整數(比如int8)向量(儲存的最佳化比是4X))和向量量化(Vector Quantization,區別於 scalar quantization 是分別從每一個維度進行壓縮,vector quantization 是透過將原始向量當做一個整體去看待,將一個向量對映到指定的參考向量裡),每種量化方式都需要權衡查詢速度和精度。

    量化特別適用於管理大規模資料庫的應用(儲存和記憶體效率至關重要),這種方法在需要在查詢速度和準確性之間取得平衡的環境中表現出色,這使得它非常適合對速度敏感且可以容忍一定精度損失的應用程式。但它不適合需要高度精確和最小化資訊損失的場景,如精確科學研究。

  • HNSW(Hierarchical Navigable Small World,分層可導航小世界)圖:HNSW是一種構建分層圖的索引策略,每層代表資料集中的一個不同粒度。查詢從頂層開始,頂層具有較少、更遠的點,然後向下移動到更詳細的層級。這種方式可以快速遍歷資料集,透過快速縮小相似向量的候選集合,大大減少了搜尋時間。

    image

    HNSW圖很好地均衡了查詢速度和準確性,使其非常適於需要立即響應的實時查詢應用和推薦系統。它們在中大型資料集中表現良好,提供可擴充套件的搜尋能力。但對於超大型資料集來說,其記憶體需求可能會成為瓶頸,因此這種方式不適用於記憶體資源受限或資料集大小遠超實際記憶體容量的場景。

  • 倒排檔案索引(Inverted File Index,IVF):IVF使用k-means這樣的演算法將向量空間分為預定義數量的幾個聚類。每個向量分配給最近的聚類,在一個搜尋中,只會涉及到最相關聚類中的向量。這種方式降低了搜尋空間,提升了查詢速度。透過將IVF和其他技術(如IVFADC-Inverted File Index with Asymmetric Distance Computation, 就結合了IVF和Quantization)相結合,可以透過進一步降低距離計算成本來提升效能。

IVF方法適合處理可擴充套件查詢環境中的高緯度資料,可以透過對相似項劃分聚類來有效降低搜尋空間,特別適用於相對靜態的資料庫,且可以允許偶爾重新聚類的場景。但對於低緯資料(可能存在過分分割)或要求儘可能低延遲的應用(聚類過程和跨多個聚類查詢可能會引入額外的查詢時間)來說,IVF可能不是最佳的選擇。

最佳化的其他注意事項
  • 降低維度:在應用索引策略之前,可以嘗試降低向量維度。像PCA或自編碼器這樣的技術可以在降低其複雜性的同時保持資料的關鍵特性,以此提升搜尋操作的效率和索引速度。
  • 並行處理:很多索引策略都可以透過多CPU核或GPU技術同步執行。這種並行處理能力可以同時處理多個請求,大大提升了吞吐量並降低了大型應用的響應時間。
  • 動態索引:對於需要經常更新資料的資料庫來說,動態索引策略可以在不需要大量重新組織索引的情況下有效插入或刪除向量。這種方式確保資料庫可以隨著時間的推移保持響應並保持最小的效能下降。

透過這些索引策略和注意事項來提升向量資料庫的效能需要深刻理解底層資料以及特定的應用需求。透過仔細選擇並調節這些策略,開發者可以顯著提高基於向量的應用的響應能力和可擴充套件性,保證其滿足真實使用場景。

用向量資料庫豐富LLM的上下文

像Facebook的 LLama2 或 TIIUAE的 Falcon大語言模型具有先進的人工智慧和類人類的文字生成能力,但由於它們是基於廣泛、通用的資料集進行訓練的,因此缺乏對特定上下文的處理能力。

可以使用如下兩種方式解決上下文限制的問題:

  • 針對性訓練:這種方式需要基於特定領域的資料集進行再訓練或微調LLM。雖然這種方式可以顯著增強模型在特定主題或行業的專業知識,但不適合大部分組織和個人。其原因包括與訓練所需的高成本計算資源,以及有效再訓練此類複雜模型所需的專業知識:有效再訓練LLMs需要深刻理解機器學習、自然語言處理以及所討論的模型的具體架構。
  • 透過向量資料庫來整合上下文:或者,LLM可以透過使用向量資料庫來直接擴充套件上下文。此時,向量資料庫儲存了特定的資訊,如向量嵌入,LLM可以檢索並使用這些資訊來增強其響應能力。透過這種方式可以納入相關的專業知識,而無需進行大量再訓練,特別適用於訓練資源匱乏的組織或個人。它使用已有的模型,同時提供了目標明確的上下文洞察力。

第二種方式稱為RAG,我們將在下一章中詳細介紹。

image

使用Falcon-7BChromaDB構建一個封閉式問題機器人

本章中,我們將介紹如何使用向量資料庫來構建一個LLM,是使用的模型是一個封閉式問題機器人(Closed Q&A bot),該機器人使用一組整合的技術元件來有效解答與科學相關的問題。

  1. databricks-dolly-15k HuggingFace Dataset:由Databricks員工生成的一個開源指令跟蹤記錄資料集。該資料集專門用於訓練大型語言模型、合成資料生成和資料增強,其中包含各種型別的提示和響應,如頭腦風暴、分類、封閉式QA、生成、資訊抽取、開放式QA和總結等。
  2. 使用Chroma作為向量儲存量:我們使用Chroma作為主要的向量儲存,作為機器人的知識庫。
  3. 語義搜尋的句子轉換器:我們使用了來自Sentence Transformers的multi-qa-MiniLM-L6-cos-v1模型,該模型特別適用於語義查詢應用,負責生成嵌入向量,並將其儲存在Chroma中。
  4. Falcon 7B Instruct 模型:作為我們的開源通用模型,Falcon 7B是一個由TII開發的一個僅包含70億引數的解碼器模型,Falcon 7B模型使用了一個龐大的名為RefinedWeb的1,500B tokens資料集進行訓練,並補充了經過篩選的語料庫。值得注意的是,Falcon 40B作為Falcon 7B的更大版本,在Hugging Face的Open LLM排行榜中名列前茅,是最頂尖的大型語言模型之一。

實現架構如下。使用multi-qa-MiniLM-L6-cos-v1作為嵌入模型,負責將資料集的資料匯入資料庫。

在使用者提問時,會使用嵌入模型對提問文字進行編碼,生成向量資料庫可以理解的格式,並由向量資料庫返回應答。Falcon 7B模型用於最佳化向量資料庫的應答結果。

下圖有點問題,看起來請求和應答是非同步執行的,但程式碼中是序列的。

image

配置環境

為了實現本文的程式碼,需要安裝如下庫:

!pip install -qU \
    transformers==4.30.2 \
    torch==2.0.1+cu118 \
    einops==0.6.1 \
    accelerate==0.20.3 \
    datasets==2.14.5 \
    chromadb \
    sentence-transformers==2.2.2

本程式碼執行在Qwak的Workspaces的一臺gpu.a10.2xl 例項上。需要注意的是執行Falcon-7B-Instruct模型的程式碼可能會根據硬體配置而變化。

構建"知識庫"

一開始,我們需要拉取Databricks-Dolly資料集,重點關注closed_qa一類的資料集。這些資料條目通常以對精確資訊的需求為特徵,這種轉一性給通用性大語言模型的訓練帶來了一定挑戰。

databricks/databricks-dolly-15kdataset card中可以檢視支援的split和過濾欄位。

from datasets import load_dataset

# Load only the training split of the dataset
# 載入 train 型別的資料集
train_dataset = load_dataset("databricks/databricks-dolly-15k", split='train')

# 透過過濾拉取 category為 closed_qa 的一組資料
closed_qa_dataset = train_dataset.filter(lambda example: example['category'] == 'closed_qa')

print(closed_qa_dataset[0])

資料庫通常可以分為三類:training、validating和evaluating。分別用於訓練、校驗和評估模型。

典型的資料集條目的格式如下:

{
  "instruction": "When was Tomoaki Komorida born?",
  "context": "Komorida was born in Kumamoto Prefecture on July 10, 1981. After graduating from high school, he joined the J1 League club Avispa Fukuoka in 2000. His career involved various positions and clubs, from a midfielder at Avispa Fukuoka to a defensive midfielder and center back at clubs such as Oita Trinita, Montedio Yamagata, Vissel Kobe, and Rosso Kumamoto. He also played for Persela Lamongan in Indonesia before returning to Japan and joining Giravanz Kitakyushu, retiring in 2012.",
  "response": "Tomoaki Komorida was born on July 10, 1981.",
  "category": "closed_qa"
}

下面,我們將重點為每組指令及其各自的上下文生成詞嵌入,並將它們整合到向量資料庫ChromaDB中。

Chroma DB是一個開源的向量資料庫系統,擅長管理向量嵌入,專為語義查詢引擎之類的應用量身定做,這種能力在自然語言處理和機器學習領域至關重要。Chroma DB作為一個記憶體型資料庫,支援資料的快速訪問和高速處理。其友好的Python設定增強了對我們專案的吸引力,簡化了與我們工作流程的整合。更多參見Chroma DB 文件

image

為了給回答生成嵌入向量,我們使用了multi-qa-MiniLM-L6-cos-v1 模型,該模型專為語義搜尋場景而訓練。給定一個問題/搜尋請求,該模型可以找到相關的文字資訊,這正是我們所需要的。

在下面例子中,解釋瞭如何將嵌入向量儲存在Chroma的記憶體集合中。

import chromadb
from sentence_transformers import SentenceTransformer

class VectorStore:

    def __init__(self, collection_name):
       # 初始化嵌入模型和向量資料庫
        self.embedding_model = SentenceTransformer('sentence-transformers/multi-qa-MiniLM-L6-cos-v1')
        self.chroma_client = chromadb.Client()
        self.collection = self.chroma_client.create_collection(name=collection_name)

    # 該方法用於將輸入的資料集轉換為向量,並儲存到向量資料庫中
    def populate_vectors(self, dataset):
        for i, item in enumerate(dataset):
            combined_text = f"{item['instruction']}. {item['context']}"
            embeddings = self.embedding_model.encode(combined_text).tolist()
            self.collection.add(embeddings=[embeddings], documents=[item['context']], ids=[f"id_{i}"])

    # 該方法直接從向量資料庫中與查詢相關的上下文
    def search_context(self, query, n_results=1):
        query_embeddings = self.embedding_model.encode(query).tolist()
        return self.collection.query(query_embeddings=query_embeddings, n_results=n_results)


# Example usage
if __name__ == "__main__":
   # Initialize the handler with collection name
    vector_store = VectorStore("knowledge-base")
    
    # 輸入上一步拉取的 closed_qa 資料集
    vector_store.populate_vectors(closed_qa_dataset)

對於每個資料集條目,我們會生成一個結合了instructioncontext欄位的嵌入向量,在我們的LLM提示中,context充當要檢索的文件。

下面,我們使用Falcon-7b-instruct LLM來生成封閉式資訊查詢的應答(無需額外的上下文),並展示知識庫的重要性。

生成基本的回答

為了實現文字生成任務,我們採用了來自Hugging Face的Falcon-7b-instruct模型。該模型是Falcon的革新系列,由Abu Dhabi的Technology Innovation Institute開發。

Falcon-7B-Instruct引人注目的一點是其有效平衡了高階功能和管理大小,用於理解複雜的文字和生成任務,其效能可以與更大的封閉原始碼模型相媲美,但包更精簡。這也是它成為我們理想選擇的原因(可以深入語言理解,但不會帶來跟更大模型一樣的開銷)。

如果你打算執行Falcon-7B-Instruct模型(無論是本地機器或遠端伺服器),需要注意硬體要求。在HuggingFace的文件中有提到,該模型最少需要16GB的記憶體來滿足最佳的效能和更快的響應次數,最好使用GPU。


import transformers
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

class Falcon7BInstructModel:

    def __init__(self):
        # 初始化模型
        model_name = "tiiuae/falcon-7b-instruct"
        self.pipeline, self.tokenizer = self.initialize_model(model_name)

    def initialize_model(self, model_name):
        # 初始化 Tokenizer
        tokenizer = AutoTokenizer.from_pretrained(model_name)

        # Pipeline setup for text generation
        pipeline = transformers.pipeline(
            "text-generation",
            model=model_name,
            tokenizer=tokenizer,
            torch_dtype=torch.bfloat16,
            trust_remote_code=True,
            device_map="auto",
        )

        return pipeline, tokenizer

    def generate_answer(self, question, context=None):
        # Preparing the input prompt
        prompt = question if context is None else f"{context}\n\n{question}"

        # 生成應答
        sequences = self.pipeline(
            prompt,
            max_length=500,
            do_sample=True,
            top_k=10,
            num_return_sequences=1,
            eos_token_id=self.tokenizer.eos_token_id,
        )

        # Extracting and returning the generated text
        return sequences['generated_text']

可以參考Hugging Face的文件來編寫上述程式碼:

  • tokenizer是自然語言處理(NLP)中的一個重要元件,它的主要用於將輸入文字轉換為一個模型可以理解的格式。本質上講,它將文字切分為更小的單元,稱為tokens。根據tokenizer的設計,這些tokens可以是單詞、子單詞(subword)甚至是特徵。在Falcon-7B-Instruct模型的上下文中,AutoTokenizer.from_pretrained(model)呼叫會載入一個專為該模型設計的tokenizer,保證能夠遵循該模型訓練的方式來將文字轉化為token。

  • pipeline是transformers庫中的一個高階工具,它對處理資料和從模型獲取預測所涉及的大部分複雜性進行了抽象。其內部分為多個步驟,如將輸入文字劃分為token、將token輸入到模型以及將模型的輸出處理為人類可讀的格式。在上述指令碼中,pipeline被設定為"text-generation",用於將一個提示(如使用者問題)作為輸入,然後根據提示生成一個連續的文字。

使用方式如下:

# 初始化 Falcon 模型類
falcon_model = Falcon7BInstructModel()

user_question = "When was Tomoaki Komorida born?"

# LLM使用LLM來為使用者提問生成回答
answer = falcon_model.generate_answer(user_question)

print(f"Result: {answer}")

你可能已經猜到模型的輸出:

{ answer: “I don't have information about Tomoaki Komorida's birthdate.” }

使用Falcon-7B-Instruct時,如果沒有相關的上下文,就會返回一個否定響應。這說明需要進一步豐富背景知識,為非一般性問題提供更有針對性和有用的答案。

生成上下文相關的回答

下面我們透過向量儲存來為模型提供相關上下文,從而提升模型能力。

有趣的是,我們使用相同的VectorStore類來同時生成嵌入向量和從使用者提問中獲取上下文。

# Assuming vector_store and falcon_model have already been initialized

# 從 VectorStore 中獲取上下文(假設資料集已經匯入資料庫)
context_response = vector_store.search_context(user_question)

# 從向量資料庫的響應中抽取上下文,上下文應該是 context 關鍵字的第一個元素
context = "".join(context_response['context'][0]) 

# Falcon模型結合提取的上下文生成應答
enriched_answer = falcon_model.generate_answer(user_question, context=context)

print(f"Result: {enriched_answer}")

最後可以得到準確的回答:

Tomoaki Komorida was born on July 10, 1981.

TIPs:

  • 如何從開源查詢適合自己的模型和資料集?
    Hugging Face上的ModelsDataset都按照任務型別進行了劃分,在找到感興趣的task之後,還可以依據Trending、Most likes、Most downloads、Recently created和Recently Updated進行排序查詢。

  • 如何編寫模型程式碼?

    huggingface的各個模組都提供了對應的Example,參考編寫即可。

相關文章