返回文章列表

動態內容搜尋技術詞彙與神經檢索解析

本文探討動態內容搜尋技術,比較詞彙檢索和神經檢索的優缺點,並提供程式碼範例說明如何使用 Jaccard 相似度、TF*IDF、BM25 和嵌入模型進行搜尋。文章涵蓋檔案片段化、向量檢索技術和 RAG 應用,最後以建立個人化書籍推薦系統為例,展示如何結合 FAISS 和 OpenAI 嵌入模型實作 RAG

Web 開發 機器學習

大語言模型時代,精準的動態內容檢索至關重要。傳統的詞彙檢索方法,例如 Jaccard 相似度,計算速度快,但準確度受限。進階的 TF*IDF 和 BM25 雖提升了準確性,卻需要預先計算詞頻,應用場景受限。神經檢索利用嵌入模型將文字轉換為向量,能更好地捕捉語義,提升搜尋精確度。實務上,需注意檔案片段化策略,兼顧模型限制、觀點完整性和提示詞長度。嵌入模型的選擇也需考慮託管服務或自建模型的優劣,以及模型的適用性。向量儲存和檢索方面,FAISS 和 Pinecone 等工具提供了高效的解決方案。

詞彙檢索與神經檢索:動態內容搜尋技術解析

在大語言模型(LLM)的時代,動態內容的檢索技術顯得尤為重要。詞彙檢索(Lexical Retrieval)是最直接的檢索方法,而神經檢索(Neural Retrieval)則提供了更為精確的語義搜尋能力。

詞彙檢索技術

詞彙檢索的核心思想是計算搜尋字串與文字片段之間的相似度。最簡單的方法是使用 Jaccard 相似度。圖 5-11 展示瞭如何將動態上下文切割成短片段,並計算每個片段與搜尋文字之間的 Jaccard 相似度。

Jaccard 相似度的計算步驟:

  1. 預處理:移除停用詞(如「的」、「是」等常見但無實際意義的詞彙),並進行詞幹提取(如將 “walking”、“walks”、“walked” 轉換為 “walk”)。

  2. 計算 Jaccard 相似度:計算兩個文字之間的共同詞彙數量,並除以它們的總詞彙數量,得到一個介於 0 和 1 之間的相似度分數。

def jaccard_similarity(query, document):
    query_set = set(query.split())
    document_set = set(document.split())
    intersection = query_set.intersection(document_set)
    union = query_set.union(document_set)
    return len(intersection) / len(union)

# 使用範例
query = "backpacking is fun"
document = "I love backpacking in the mountains"
print(jaccard_similarity(query, document))

內容解密:

  1. jaccard_similarity 函式:該函式接受兩個字串引數 querydocument,並計算它們的 Jaccard 相似度。
  2. set(query.split()):將輸入字串按空格分割成單詞,並轉換為集合,去除重複詞彙。
  3. intersection 方法:計算兩個集合的交集,即共同詞彙。
  4. union 方法:計算兩個集合的聯集,即所有唯一詞彙的總和。
  5. 相似度計算:交集大小除以聯集大小,得出相似度。

Jaccard 相似度的優勢在於實作簡單、無需預先索引,且在小型檔案集上執行速度快。然而,它無法區分常見詞和具有特定含義的詞彙,如 “go” 和 “backpacking” 的匹配權重相同,這限制了其精確度。

詞頻-逆檔案頻率(TF*IDF)與 BM25

為了克服 Jaccard 相似度的侷限性,更先進的方法如 TF*IDFBM25 被提出,這些技術透過權重調整來提高罕見詞匹配的重要性,從而提升檢索的準確性。然而,這些方法需要預先計算詞彙在檔案中的出現頻率,這在某些應用場景中可能不可行。

神經檢索技術

神經檢索透過使用嵌入模型(Embedding Model)將文字轉換為高維向量,能夠更好地捕捉語義資訊。具有相似含義的文字片段,其對應的向量在嵌入空間中會彼此接近。

神經檢索的流程:

  1. 檔案索引

    • 將檔案分割成小片段。
    • 使用嵌入模型將片段轉換為向量。
    • 將向量儲存在向量資料函式庫中。
  2. 查詢處理

    • 取得查詢字串。
    • 將查詢字串轉換為向量。
    • 在向量資料函式庫中搜尋與查詢向量最近的向量,並傳回對應的文字片段。
from sentence_transformers import SentenceTransformer
import numpy as np

# 初始化嵌入模型
model = SentenceTransformer('all-MiniLM-L6-v2')

def neural_retrieval(query, documents):
    # 將查詢和檔案轉換為向量
    query_vector = model.encode(query)
    document_vectors = model.encode(documents)
    
    # 計算餘弦相似度
    similarities = np.dot(document_vectors, query_vector) / (np.linalg.norm(document_vectors, axis=1) * np.linalg.norm(query_vector))
    
    # 傳回最相似的檔案索引
    return np.argmax(similarities)

# 使用範例
query = "I am looking for information on backpacking."
documents = ["Backpacking is a popular outdoor activity.", "The weather today is sunny."]
print(neural_retrieval(query, documents))

內容解密:

  1. SentenceTransformer 模型:使用預訓練的句子嵌入模型將文字轉換為向量表示。
  2. encode 方法:將輸入文字轉換為固定維度的向量。
  3. 餘弦相似度計算:透過向量的點積和範數計算查詢向量與檔案向量之間的相似度。
  4. argmax 方法:傳回相似度最高的檔案索引。

神經檢索克服了詞彙檢索中的許多限制,能夠處理同義詞、錯別字和語言障礙,提供更準確的搜尋結果。

檔案片段化與向量檢索技術在RAG應用中的實作

在開發RAG(檢索增強生成)應用程式時,檔案片段化(Snippeting)是至關重要的步驟。所謂檔案片段化,是指將可搜尋的檔案切割成適合搜尋的適當大小的片段。選擇片段大小時,需要考慮以下三個關鍵標準:

  1. 確保Token數量低於嵌入模型的最大限制:截至2024年,OpenAI的嵌入模型的最大Token數量限制為8,191。因此,在進行檔案片段化時,必須確保每個片段的Token數量不超過此限制。
  2. 保持每個片段包含一個主要觀點:理想情況下,每個文字片段應該包含且只包含一個主要觀點。如果一個片段過大,涵蓋了多個不同的主題,那麼所產生的向量可能會處於這些主題之間的某個點,從而降低搜尋的準確性。
  3. 確保片段大小適合放置在提示中:片段的大小應該適中,既不能太大,也不能太小,以確保能夠完整地呈現相關資訊。

檔案片段化的實作方法

有多種方法可以實作檔案片段化,以下是兩種常見的方法:

1. 移動視窗法

移動視窗法是透過選擇一個視窗大小(例如256個詞)和步長(例如128個詞)來實作檔案片段化的。具體步驟如下:

  • 首先,選定視窗大小和步長。
  • 然後,從檔案的起始位置開始,擷取第一個視窗大小的文字作為第一個片段。
  • 接著,按照步長向前移動,擷取下一個視窗大小的文字作為下一個片段。
  • 重複此過程,直到檔案的末尾。

這種方法的優點是可以控制視窗之間的重疊度,以確保重要的資訊不會被截斷。然而,重疊度過高可能會導致片段數量增加,從而增加儲存成本。

2. 按照自然邊界進行分割

另一種方法是根據檔案的自然邊界,如段落或章節進行分割。這種方法可以確保每個片段包含一個完整的主題,避免了因視窗大小不當而導致的資訊截斷。

增強片段內容

在某些情況下,可以透過新增額外的上下文資訊來增強片段的內容。例如,如果一個程式碼片段是一個類別中的方法,那麼將類別定義、初始化程式碼等相關資訊納入片段中,可以為嵌入模型提供更多的上下文,從而產生更好的向量表示。

嵌入模型的選擇

嵌入模型是用於將文字或程式碼轉換為向量的模型。與大語言模型(LLM)不同,嵌入模型專注於生成能夠代表輸入文字語義的向量。選擇合適的嵌入模型需要考慮以下幾個因素:

  • 使用託管模型或自建模型:可以使用像OpenAI提供的託管嵌入模型,也可以自行佈署嵌入模型。自建模型可以減少網路延遲和成本。
  • 模型的適用性:大多數現代嵌入模型都同時支援文字和程式碼。如果有特定的需求,如處理特殊的自然語言或程式語言,可能需要選擇更合適的模型。

向量儲存與檢索

嵌入模型生成的向量通常具有很高的維度(約1000維)。高效地儲存和檢索這些向量是一個具有挑戰性的任務。幸運的是,有一些成熟的函式庫,如FAISS,可以提供快速的向量檢索功能。此外,也可以使用像Pinecone.io這樣的軟體即服務(SaaS)解決方案,它們提供了可擴充套件的向量儲存和檢索服務。

實作簡單的RAG應用

本文將介紹如何建立一個基本的RAG應用程式。該應用程式將實作檔案片段化、向量生成、向量儲存和檢索等功能。雖然這個範例應用程式可能不是最佳化的,但它涵蓋了RAG應用程式的基本組成部分,為開發更複雜的應用提供了基礎。

程式碼範例:使用FAISS進行向量檢索

import numpy as np
import faiss

# 假設我們有一些向量資料
vectors = np.random.rand(100, 128).astype('float32')

# 建立FAISS索引
index = faiss.IndexFlatL2(128)

# 將向量加入索引
index.add(vectors)

# 查詢與某個向量最相似的向量
query_vector = np.random.rand(1, 128).astype('float32')
D, I = index.search(query_vector, k=5)

print("相似度距離:", D)
print("相似向量的索引:", I)

內容解密:

  1. 匯入必要的函式庫:首先,我們匯入了numpy用於數值計算,以及faiss用於向量檢索。
  2. 產生範例向量資料:我們產生了100個128維的隨機向量作為範例資料。
  3. 建立FAISS索引:使用faiss.IndexFlatL2建立了一個根據歐幾裡得距離(L2距離)的索引。
  4. 新增向量到索引:將產生的隨機向量加入到FAISS索引中。
  5. 查詢相似向量:產生了一個查詢向量,並使用FAISS索引查詢了最相似的5個向量。
  6. 輸出結果:輸出了相似向量的距離和索引。

這個範例展示瞭如何使用FAISS進行高效的向量檢索。在實際應用中,可以將嵌入模型生成的向量儲存在FAISS索引中,以實作快速的相似性搜尋。

使用RAG技術建立個人化書籍推薦系統

在現代線上書店中,提供個人化的書籍推薦已成為提升使用者經驗的重要手段。本文將介紹如何利用檢索增強生成(Retrieval-Augmented Generation, RAG)技術,根據使用者過去的書評預測他們對新書的喜好程度。

RAG技術的核心流程

RAG技術結合了檢索和生成的優勢,能夠根據使用者的歷史資料檢索相關資訊,並生成預測結果。其核心流程包括:

  1. 索引建立:將使用者的歷史書評轉換為向量並建立索引。
  2. 檢索:當使用者瀏覽新書時,檢索與該書相關的使用者歷史書評。
  3. 生成預測:將檢索到的書評與新書資訊結合,生成使用者對新書的評分預測。

實作RAG技術的步驟

1. 匯入必要的函式庫並初始化OpenAI客戶端

import numpy as np
import faiss
from openai import OpenAI
client = OpenAI()

2. 蒐集使用者的歷史書評資料

reviews = [
    "I hate stories about backpacking. It's boring.",
    "A moving exploration of racial injustice and moral growth.",
    "Compelling dystopia, but overwhelmingly bleak.",
    "Timeless romance with sharp social commentary.",
    "Epic sea adventure with philosophical depth.",
    "Mesmerizing magic and romance with rich world-building.",
    "Beautifully descriptive, but predictable plot.",
    "A detailed and emotional journey through loss and art.",
    "Fresh take on Greek mythology, but pacing dragged.",
    "Brilliant exploration of complex relationships and personal growth.",
    "Another bland romantic utopia. This time on a tropical island.",
]

3. 定義取得文字嵌入向量的函式

def get_embedding(text):
    text = text.replace("\n", " ")
    return client.embeddings.create(
        input=[text],
        model="text-embedding-3-small",
    ).data[0].embedding

內容解密:

此函式用於將輸入的文字轉換為嵌入向量。首先,它會移除文字中的換行符,以確保文字的連貫性。接著,利用OpenAI的嵌入模型(text-embedding-3-small)生成嵌入向量。該向量能夠捕捉文字的語義資訊,為後續的相似度計算奠定基礎。

4. 建立索引函式以檢索相關書評

def index_reviews(reviews):
    vectors = []
    for review in reviews:
        vectors.append(get_embedding(review))
    d = len(vectors[0])
    index = faiss.IndexFlatL2(d)
    vectors = np.array(vectors).reshape(len(vectors), -1)
    index.add(vectors)
    return index

def retrieve_reviews(index, query, reviews, k=2):
    query_vector = get_embedding(query)
    query_vector = np.array(query_vector).reshape(1, -1)
    distances, indices = index.search(query_vector, k)
    return [reviews[i] for i in indices[0]]

內容解密:

index_reviews函式負責將使用者的歷史書評轉換為向量,並利用FAISS函式庫建立L2距離索引,以便進行高效的相似度檢索。retrieve_reviews函式則根據查詢文字取得相關的歷史書評。它首先將查詢文字轉換為嵌入向量,然後在索引中搜尋最相似的書評向量,並傳回對應的原始書評文字。

5. 測試檢索功能

index = index_reviews(reviews)
book = "The Beach by Alex Garland critiques backpacker culture by exposing the selfishness and moral decay behind their pursuit of an untouched paradise."
related_reviews = retrieve_reviews(index, book, reviews)
print(related_reviews)

輸出結果:

['I hate stories about backpacking. It\'s boring.', 'Another bland romantic utopia. This time on a tropical island.']

內容解密:

這段程式碼首先建立了使用者歷史書評的索引,然後對新書進行檢索,找出與新書最相關的兩條歷史書評。輸出的結果顯示,使用者曾經表示厭惡關於揹包旅行的故事,並且對浪漫烏託邦型別的故事不感興趣,這些資訊將用於後續的評分預測。

6. 定義預測使用者評分的函式

def predict_rating(book, related_reviews):
    reviews = "\n".join(related_reviews)
    prompt = (
        "Here is a book I might want to read:\n" +
        book + "\n\n" +
        "Here are relevant reviews from the past:\n" +
        reviews + "\n\n" +
        "On a scale of 1 (worst) to 5 (best), " +
        "how likely am I to enjoy this book? " +
        "Reply with no explanation, just a number."
    )
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=2000,
        temperature=0.7,
    )
    return response.choices[0].message.content

內容解密:

此函式透過組合新書資訊和相關歷史書評,構建了一個提示詞,並利用GPT模型預測使用者對新書的評分。提示詞中明確要求模型僅輸出一個數字評分,而不提供任何解釋。這種設計使得輸出結果簡潔明瞭,便於直接使用。

7. 執行RAG應用預測使用者評分

predict_rating(book, related_reviews)

最終生成的提示詞如下:

Here is a book I might want to read:
The Beach by Alex Garland critiques backpacker culture by exposing the selfishness and moral decay behind their pursuit of an untouched paradise.
Here are relevant reviews from the past:
I hate stories about backpacking. It's boring.
Another bland romantic utopia. This time on a tropical island.
On a scale of 1 (worst) to 5 (best), how likely am I to enjoy this book?
Reply with no explanation, just a number.

模型預測使用者對《海灘》的評分為2,表明使用者很可能不喜歡這本文。

神經檢索與詞法檢索的比較

目前大多數RAG應用採用神經檢索技術,但詞法檢索也是一種可行的替代方案。詞法檢索方法經過數十年的發展,已相當成熟,並在大多數線上搜尋體驗中發揮作用。在某些場景下,詞法檢索甚至可能更具優勢。未來,可以根據具體需求選擇合適的檢索技術,以最佳化RAG應用的效能。