隨著資料量的爆炸式增長,如何有效地檢索和利用資訊成為一個關鍵挑戰。Retrieval Augmented Generation (RAG) 技術應運而生,它結合了資訊檢索和自然語言生成,為處理大量文字資料提供了新的解決方案。RAG 的核心思想是根據使用者查詢,從大型資料函式庫中檢索相關資訊,並生成高品質的文字輸出。這篇文章將深入探討 RAG 的關鍵技術,包括向量儲存、檢索和相似度計算,並提供 Python 程式碼範例。理解這些技術對於構建高效的資訊檢索和生成系統至關重要,特別是在處理大量文字資料和複雜查詢時,RAG 能夠提供更精準、更豐富的結果。

資料結構

db_records 列表包含了多個字串,每個字串代表了一個句子或是一段文字。這些文字可能來自不同的來源,如書籍、文章或網頁內容。

資料檢視

為了方便檢視資料,我們可以使用 Python 的 textwrap 模組來將長文字進行換行,使其更容易閱讀。

import textwrap

# 定義文字換行的寬度
wrapper = textwrap.TextWrapper(width=80)

# 對每個記錄進行換行
wrapped_records = [wrapper.fill(record) for record in db_records]

# 列印出換行後的記錄
for record in wrapped_records:
    print(record)
    print("---------------\n")

資料檢索系統

在建立了一個基本的資料結構和檢視機制後,我們可以開始構建一個簡單的資料檢索系統。這個系統將允許使用者輸入查詢並傳回相關的結果。

def retrieve_data(query):
    # 進行查詢並傳回相關結果
    results = [record for record in db_records if query in record]
    return results

# 測試查詢功能
query = "Retrieval Augmented Generation"
results = retrieve_data(query)

# 列印出查詢結果
for result in results:
    print(result)
    print("---------------\n")

未來擴充套件

在未來的章節中,我們將繼續深入探討資料檢索和生成的更多高階主題,包括如何使用更先進的技術如 RAG(Retrieval Augmented Generation)來提高查詢的精確度和生成更相關的結果。

圖表翻譯:

  graph LR
    A[資料收集] --> B[資料前處理]
    B --> C[資料儲存]
    C --> D[資料檢索]
    D --> E[查詢結果]

此圖表展示了資料從收集到檢索的流程,包括前處理、儲存和最終的查詢結果。

Retrieval Augmented Generation(RAG)簡介

RAG是一種先進的生成式人工智慧(AI)技術,結合了檢索和生成兩個步驟。它可以根據使用者的查詢,從大量的文字資料中檢索相關資訊,並生成高品質的文字輸出。

RAG的工作原理

RAG的工作原理是首先使用檢索器(retriever)從文字資料中找到與使用者查詢相關的文欄位落。然後,生成器(generator)根據這些相關文欄位落生成最終的文字輸出。

RAG的優點

RAG具有以下優點:

  • 能夠根據使用者的查詢生成高品質的文字輸出
  • 可以處理大量的文字資料
  • 能夠學習到使用者的偏好和興趣

RAG的應用

RAG可以應用於以下領域:

  • 對話系統:RAG可以用於生成對話系統的回應
  • 文字摘要:RAG可以用於生成文字摘要
  • 文字生成:RAG可以用於生成高品質的文字

RAG的三種組態

RAG有三種組態:naïve、advanced和modular。

Naïve RAG

Naïve RAG是最基本的RAG組態,它使用簡單的檢索和生成演算法。

Advanced RAG

Advanced RAG是比Naïve RAG更先進的組態,它使用更複雜的檢索和生成演算法。

Modular RAG

Modular RAG是最先進的RAG組態,它使用模組化的檢索和生成演算法,可以根據使用者的需求定製化。

檢索度量

檢索度量是用於評估檢索結果的相關性和準確性的指標。常用的檢索度量包括:

  • Cosine相似度:用於評估兩個向量之間的相似性
  • Jaccard相似度:用於評估兩個集合之間的相似性

檢索度量的計算

檢索度量的計算可以使用以下公式:

  • Cosine相似度:cosine similarity = (A * B) / (|A| * |B|)
  • Jaccard相似度:jaccard similarity = |A ∩ B| / |A ∪ B|

內容解密:

上述程式碼使用Python語言實作了RAG的基本功能。它首先定義了一個call_llm_with_full_text函式,用於呼叫LLM模型生成文字。然後,它定義了一個print_formatted_response函式,用於列印生成的文字。最後,它使用這兩個函式生成並列印了使用者查詢的結果。

import textwrap

def call_llm_with_full_text(query):
    # LLM模型生成文字
    llm_response = "..."
    return llm_response

def print_formatted_response(llm_response):
    # 列印生成的文字
    print(llm_response)

query = "define a rag store"
llm_response = call_llm_with_full_text(query)
print_formatted_response(llm_response)

圖表翻譯:

下圖示範了RAG的工作原理:

  flowchart TD
    A[使用者查詢] --> B[檢索器]
    B --> C[生成器]
    C --> D[文字輸出]

這個圖表展示了RAG的基本工作流程:使用者查詢 -> 檢索器 -> 生成器 -> 文字輸出。

餘弦相似度

餘弦相似度是一種用於衡量兩個向量之間角度的度量。 在我們的案例中,兩個向量分別是使用者查詢和檔案集中的每個檔案。 程式首先匯入所需的類別和函式:

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity

TfidfVectorizer 匯入的是將文字檔案轉換為 TF-IDF 特徵矩陣的類別。TF-IDF(詞頻-逆檔案頻率)是一種用於量化詞彙在檔案集中重要性的方法,它區分了常見詞彙和對特定文字有重要意義的詞彙。因此,TF-IDF 會根據詞彙在檔案中的頻率和在檔案集中的逆頻率來量化詞彙的重要性。

cosine_similarity 匯入的是用於計算向量之間相似性的函式。

然後,calculate_cosine_similarity(text1, text2) 函式計算查詢(text1)和檔案集中的每個記錄(text2)之間的餘弦相似度。該函式使用向量器將查詢文字(text1)和檔案集中的每個記錄(text2)轉換為向量,然後計算並傳回兩個向量之間的餘弦相似度:

def calculate_cosine_similarity(text1, text2):
    vectorizer = TfidfVectorizer(
        stop_words='english',
        use_idf=True,
        norm='l2',
        ngram_range=(1, 2),  # 使用單詞和雙片語合
        sublinear_tf=True,  # 應用子線性 TF 排序
        analyzer='word'  # 也可以嘗試 'char'
    )

    tfidf = vectorizer.fit_transform([text1, text2])
    similarity = cosine_similarity(tfidf[0:1], tfidf[1:2])

    return similarity[0][0]

此函式的關鍵引數包括:

  • stop_words='english':忽略常見的英語單詞,以專注於有意義的內容。
  • use_idf=True:啟用逆檔案頻率加權。
  • norm='l2':對輸出向量應用 L2 標準化。
  • ngram_range=(1, 2):考慮單詞和雙片語合。
  • sublinear_tf=True:應用子線性 TF 排序。
  • analyzer='word':在單詞層面分析文字。

然而,餘弦相似度在某些情況下可能受到限制。當處理模糊查詢時,餘弦相似度可能存在侷限性,因為它嚴格根據文字向量表示之間的角度來衡量相似度。如果使用者詢問一個模糊的問題,如「什麼是 rag?」,而資料函式庫主要包含有關「RAG」(檢索增強生成)的人工智慧資訊,而不是「rag 布」,則餘弦相似度評分可能很低。這是因為數學模型缺乏上下文理解,以區分「rag」的不同含義。它僅根據文字中單詞的存在和頻率計算相似度,而不理解使用者的意圖或查詢的更廣泛上下文。因此,即使提供的答案在技術上是正確的且可用於可用的資料集,餘弦相似度也可能不準確地反映相關性,如果查詢的上下文在資料中沒有很好地表示。

在這種情況下,我們可以嘗試使用增強相似度。

文字相似度計算與增強相似度

文字相似度計算是一種用於評估兩個文字之間語義關係的技術。增強相似度(Enhanced Similarity)是一種利用自然語言處理工具來捕捉詞彙之間的語義關係的方法。這種方法使用像 spaCy 和 NLTK 這樣的函式庫來預處理文字,減少雜訊,擴充套件詞彙的同義詞,並根據擴充套件詞彙的語義豐富度計算相似度。

相似度計算方法

相似度計算方法包括以下幾個步驟:

  1. 取得同義詞:為給定的詞彙從 WordNet 中取得同義詞。
  2. 預處理文字:將所有文字轉換為小寫,進行詞彙根提取(lemmatization),並過濾停用詞(stopwords)和標點符號。
  3. 擴充套件詞彙:使用同義詞擴充套件給定的詞彙列表。
  4. 計算相似度:計算預處理和擴充套件後的文字向量之間的餘弦相似度。

程式碼實作

以下是計算增強相似度的主要函式:

  • get_synonyms(word):從 WordNet 中為給定的詞彙取得同義詞。
  • preprocess_text(text):預處理文字,包括轉換為小寫、lemmatization、過濾停用詞和標點符號。
  • expand_with_synonyms(words):使用同義詞擴充套件給定的詞彙列表。
  • calculate_enhanced_similarity(text1, text2):計算兩個文字之間的增強相似度,包括預處理、擴充套件同義詞和計算餘弦相似度。

程式碼片段

import spacy
import nltk
from nltk.corpus import wordnet
from collections import Counter
import numpy as np

# 載入 spaCy 模型
nlp = spacy.load("en_core_web_sm")

# 取得同義詞
def get_synonyms(word):
    synonyms = set()
    for syn in wordnet.synsets(word):
        for lemma in syn.lemmas():
            synonyms.add(lemma.name())
    return list(synonyms)

# 預處理文字
def preprocess_text(text):
    doc = nlp(text)
    words = [token.lemma_ for token in doc if not token.is_stop and not token.is_punct]
    return words

# 擴充套件詞彙
def expand_with_synonyms(words):
    expanded_words = []
    for word in words:
        synonyms = get_synonyms(word)
        expanded_words.extend(synonyms)
    return expanded_words

# 計算增強相似度
def calculate_enhanced_similarity(text1, text2):
    words1 = preprocess_text(text1)
    words2 = preprocess_text(text2)
    expanded_words1 = expand_with_synonyms(words1)
    expanded_words2 = expand_with_synonyms(words2)
    # 計算餘弦相似度
    vector1 = Counter(expanded_words1)
    vector2 = Counter(expanded_words2)
    dot_product = sum(vector1[word] * vector2[word] for word in vector1.keys() & vector2.keys())
    magnitude1 = np.sqrt(sum(count ** 2 for count in vector1.values()))
    magnitude2 = np.sqrt(sum(count ** 2 for count in vector2.values()))
    similarity = dot_product / (magnitude1 * magnitude2)
    return similarity

# 測試
text1 = "This is a test sentence."
text2 = "This sentence is for testing."
similarity = calculate_enhanced_similarity(text1, text2)
print(f"增強相似度:{similarity}")

關鍵字搜尋與匹配

為了實作最佳匹配功能,我們首先需要定義一個函式,該函式能夠根據使用者輸入的查詢和資料函式庫中的記錄進行匹配。以下是這個函式的實作:

def find_best_match_keyword_search(query, db_records):
    """
    這個函式根據使用者輸入的查詢和資料函式庫中的記錄進行匹配,
    傳回最佳匹配的記錄。

    引數:
    - query (str): 使用者輸入的查詢
    - db_records (list): 資料函式庫中的記錄列表

    傳回:
    - best_record (str): 最佳匹配的記錄
    """
    
    # 初始化最佳分數和記錄
    best_score = 0
    best_record = None

    # 將查詢分割成個別關鍵字
    query_keywords = set(query.lower().split())

    # 遍歷每個記錄在資料函式庫中
    for record in db_records:
        # 將記錄分割成關鍵字
        record_keywords = set(record.lower().split())
        
        # 計算共同關鍵字的數量
        common_keywords = query_keywords.intersection(record_keywords)
        current_score = len(common_keywords)
        
        # 更新最佳分數和記錄如果當前分數更高
        if current_score > best_score:
            best_score = current_score
            best_record = record

    return best_record

程式碼解釋

  • 函式定義find_best_match_keyword_search 函式接受兩個引數:query(使用者輸入的查詢)和 db_records(資料函式庫中的記錄列表)。
  • 初始化變數best_score 初始化為 0,best_record 初始化為 None,用於儲存最佳匹配的記錄。
  • 查詢分割:將使用者輸入的查詢分割成個別關鍵字,並轉換為小寫以確保查詢不區分大小寫。
  • 記錄遍歷:遍歷每個記錄在資料函式庫中,對每個記錄進行相同的分割和轉換操作。
  • 共同關鍵字計算:使用集合的交集操作 (intersection) 計算查詢和記錄之間的共同關鍵字數量。
  • 最佳匹配更新:如果當前的匹配分數高於目前的最佳分數,則更新最佳分數和最佳記錄。
  • 傳回最佳記錄:函式傳回最佳匹配的記錄。

範例使用

# 範例查詢和資料函式庫記錄
query = "Python programming language"
db_records = [
    "Python is a popular programming language",
    "Java is another popular language",
    "Python programming is fun and easy"
]

# 呼叫函式找到最佳匹配
best_match = find_best_match_keyword_search(query, db_records)

print("最佳匹配:", best_match)

這個範例示範瞭如何使用 find_best_match_keyword_search 函式根據使用者輸入的查詢找到資料函式庫中最佳匹配的記錄。

最佳化查詢結果的評估與增強

在前面的步驟中,我們已經實作了根據關鍵字的查詢,並計算了查詢和最佳匹配記錄之間的相似度。現在,我們將更深入地探討如何評估和增強查詢結果。

評估查詢結果

評估查詢結果的品質是確保系統效能的關鍵步驟。為此,我們使用了兩種不同的相似度指標:餘弦相似度和增強相似度。

餘弦相似度

餘弦相似度是一種常用的文字相似度衡量指標,它計算兩個向量之間的夾角餘弦。然而,在本例中,由於使用者輸入較短,而最佳匹配記錄較長,導致餘弦相似度得分偏低。

score = calculate_cosine_similarity(query, best_matching_record)
print(f"最佳餘弦相似度得分:{score:.3f}")

輸出:

最佳餘弦相似度得分:0.126

增強相似度

為了克服餘弦相似度的限制,我們引入了一種增強相似度計算方法。這種方法考慮了更多的語義因素,從而能夠更好地捕捉使用者輸入和查詢結果之間的相關性。

response = best_matching_record
print(query, ":", response)
similarity_score = calculate_enhanced_similarity(query, response)
print(f"增強相似度:{similarity_score:.3f}")

輸出:

定義一個 RAG 儲存函式庫 :A RAG 向量儲存是一個資料函式庫或資料集
增強相似度:0.642

如預期,增強相似度得分明顯高於餘弦相似度得分,表明增強相似度方法更能準確地反映使用者輸入和查詢結果之間的相關性。

增強查詢結果

除了評估查詢結果外,我們還可以透過增強使用者輸入來改善查詢體驗。具體來說,我們可以使用查詢結果來擴充原始使用者輸入,從而提供更豐富、更有用的資訊給使用者。

  flowchart TD
    A[使用者輸入] --> B[查詢]
    B --> C[查詢結果]
    C --> D[增強使用者輸入]
    D --> E[傳回增強結果]

圖表翻譯:

上述流程圖描述瞭如何從使用者輸入到查詢結果,再到增強使用者輸入的過程。首先,系統接收使用者輸入,然後進行查詢以獲得相關結果。接著,系統使用這些結果來增強原始使用者輸入,最後傳回增強後的結果給使用者。

透過這種方式,不僅能夠提高查詢結果的相關性和準確性,也能夠提供給使用者更全面的資訊和更好的使用體驗。

RAG 向量儲存:基礎與進階概念

RAG(Retrieval-Augmented Generation)向量儲存是一種專門設計的資料函式庫或資料集,用於儲存向量化的資料點。這種儲存方式可以大大提高搜尋效率和處理速度,特別是在處理大量檔案和複雜內容時。

基礎 RAG 概念

基礎 RAG 可以在許多情況下提供足夠的效能。但是,當檔案數量增大或內容變得更加複雜時,進階 RAG 組態可以提供更好的結果。進階 RAG 的一個關鍵方面是使用索引來減少計算負載,使搜尋過程更加高效。

進階 RAG:向量搜尋和索引基礎搜尋

進階 RAG 涉及將文字資料轉換為數值表示,從而增強搜尋效率和處理速度。與傳統方法直接解析文字不同,RAG 首先將檔案和使用者查詢轉換為向量,即數值形式,可以加速計算。向量可以是簡單的詞彙頻率計數,也可以是捕捉更深層次語言模式的嵌入。

向量搜尋

向量搜尋涉及將使用者查詢和檔案轉換為數值向量。透過比較這些向量,可以快速找到最相關的檔案。這種方法在處理大量資料時尤其有效,因為它可以使用數學計算快速檢索相關資料。

索引基礎搜尋

索引基礎搜尋使用 TF-IDF(詞彙頻率-逆檔案頻率)等統計措施來評估一個詞彙對於檔案集合的重要性。這些向量作為矩陣中的索引,允許快速比較相似性而不需要完全解析每個檔案。

實作向量搜尋和索引基礎搜尋

實作向量搜尋和索引基礎搜尋需要將檔案和使用者查詢轉換為數值向量。這可以透過計算詞彙頻率、嵌入或其他語言模式來完成。然後,透過比較這些向量,可以找到最相關的檔案。

示例:查詢最佳匹配

以下是一個簡單的示例,展示如何實作查詢最佳匹配:

def find_best_match(text_input, records):
    best_score = 0
    best_record = None
    for record in records:
        current_score = calculate_cosine_similarity(text_input, record)
        if current_score > best_score:
            best_score = current_score
            best_record = record
    return best_record

這個示例使用餘弦相似度計算來比較使用者查詢和檔案之間的相似性。最佳匹配的檔案是具有最高相似度分數的檔案。

內容解密:

if current_score > best_score:
    best_score = current_score
    best_record = record
return best_score, best_record

這段程式碼是用於更新最佳記錄和最佳分數的。當 current_score 大於 best_score 時,會更新 best_scorebest_record 的值。這個過程是在尋找最佳匹配的記錄時使用的。

圖表翻譯:

  flowchart TD
    A[開始] --> B[計算分數]
    B --> C[比較分數]
    C --> D[更新最佳記錄]
    D --> E[傳回最佳記錄]

這個流程圖描述了程式碼的邏輯流程。首先計算分數,然後比較分數,如果需要更新最佳記錄,最後傳回最佳記錄。

內容解說:

在這個程式碼中,我們使用了 find_best_match 函式來尋找最佳匹配的記錄。這個函式會傳回最佳匹配的記錄和相似度分數。然後,我們使用 print_formatted_response 函式來顯示最佳匹配的記錄。

圖表示:

  flowchart TD
    A[使用者查詢] --> B[查詢資料函式庫]
    B --> C[傳回結果]
    C --> D[顯示結果]

這個流程圖描述了使用者查詢的流程。使用者輸入查詢,然後查詢資料函式庫,最後傳回結果並顯示給使用者。

技術深度:

在這個程式碼中,我們使用了向量搜尋技術來尋找最佳匹配的記錄。向量搜尋技術可以捕捉到複雜檔案中的細微差別,這使得它比傳統的關鍵字搜尋更有效。

未來,我們可以繼續改進向量搜尋技術,使其更能夠捕捉到檔案中的細微差別。另外,我們也可以嘗試使用其他的搜尋技術,例如自然語言處理技術,來進一步提高搜尋的準確度。

瞭解向量搜尋和索引搜尋

向量搜尋是一種快速找到相關檔案的方法,但其效率會隨著資料集大小的增加而降低。為瞭解決這個可擴充套件性問題,索引搜尋提供了一種更先進的解決方案。索引搜尋透過比較使用者查詢的向量和檔案內容的索引向量來工作,而不是直接比較使用者查詢的向量和檔案內容的向量。

向量搜尋的限制

向量搜尋的主要限制是其效率會隨著資料集大小的增加而降低。這是因為向量搜尋需要計算使用者查詢和每個檔案之間的相似度,這個過程可能很耗時。

索引搜尋的優點

索引搜尋可以加速檔案檢索的速度。透過使用索引向量,索引搜尋可以快速找到最相關的檔案,而不需要計算每個檔案和使用者查詢之間的相似度。

實作索引搜尋

要實作索引搜尋,需要使用TF-IDF(Term Frequency-Inverse Document Frequency)向量化器將文字檔案轉換為矩陣中的TF-IDF特徵。然後,使用餘弦相似度函式計算查詢和矩陣中的加權向量之間的相似度。

TF-IDF向量化器

TF-IDF向量化器是一種將文字檔案轉換為矩陣中的TF-IDF特徵的方法。TF-IDF會根據檔案中的詞彙頻率對詞彙進行加權。

餘弦相似度函式

餘弦相似度函式是一種計算兩個向量之間相似度的方法。它透過計算兩個向量之間的餘弦值來工作。

從技術架構視角來看,本文深入淺出地介紹了資料檢索系統的構建,從簡單的關鍵字匹配到根據向量和索引的進階搜尋策略,層層遞進。分析段落中,我們比較了不同相似度計算方法(例如,餘弦相似度和增強相似度)的優劣,並指出了單純依靠詞頻統計的侷限性,特別是在處理模糊查詢和語義理解方面的不足。增強相似度透過匯入同義詞擴充套件和詞彙根提取等技術,有效提升了查詢的準確性,但同時也增加了計算複雜度。前瞻段落中,我們預見隨著資料量的爆炸式增長,向量搜尋的效率瓶頸將日益凸顯,根據高效索引的搜尋策略將成為主流。玄貓認為,開發者應關注向量資料函式庫和索引技術的最新進展,例如分散式向量搜尋和根據圖的索引,以構建更具擴充套件性和效能的資料檢索系統。對於追求極致效能的應用,可以考慮結合向量量化和近似最近鄰搜尋等技術,在精確度和速度之間取得平衡。