隨著資料規模的增長,動態 RAG 系統在處理和回應速度方面臨挑戰。本文介紹如何最佳化動態 RAG 系統,避免重複載入資料,並利用向量相似度搜尋技術提升查詢效率。我們將使用 Chroma 作為向量查詢引擎,並結合 spaCy 進行文字相似度評估。此外,本文還將探討如何整合 Llama 2 大語言模型,並透過微調技術提升模型效能,最終構建一個高效的自然語言處理系統。

避免在動態RAG中重複載入資料

在建立和使用動態RAG(Retrieve, Augment, Generate)系統時,避免重複載入資料是一個重要的考慮因素。以下是如何實作這一點的示例:

if not collection_exists:
    # 將支援檔案嵌入並儲存在集合中
    collection.add(
        ids=[str(i) for i in range(0, nb)],
        documents=completion_list,
        metadatas=[{"type": "completion"} for _ in range(0, nb)],
    )

在這個例子中,我們首先檢查集合是否已經存在。如果不存在,則將支援檔案嵌入並儲存在集合中。

測量回應時間

建立和儲存集合後,我們可以測量回應時間:

response_time = time.time() - start_time
print(f"回應時間:{response_time:.2f} 秒")

這個例子顯示瞭如何測量從開始建立集合到完成的時間,並將其列印預出來。

顯示嵌入

現在,讓我們顯示嵌入:

# 取得包含嵌入的集合
result = collection.get(include=['embeddings'])

# 提取第一個嵌入
first_embedding = result['embeddings'][0]

# 顯示第一個嵌入和其長度
print("第一個嵌入:", first_embedding)
print("嵌入長度:", len(first_embedding))

這個例子顯示瞭如何取得包含嵌入的集合,提取第一個嵌入,並顯示其值和長度。

查詢集合

最後,讓我們查詢集合:

# 執行查詢對集合的語義搜尋功能
query_result = collection.query(...)

這個例子顯示瞭如何執行查詢對集合的語義搜尋功能。

圖表翻譯:
  graph LR
    A[開始] --> B[建立集合]
    B --> C[測量回應時間]
    C --> D[顯示嵌入]
    D --> E[查詢集合]

這個圖表顯示了建立和使用動態RAG系統的步驟之間的關係。

文字相似度分析與評估

在進行文字相似度分析之前,我們需要了解什麼是文字相似度。文字相似度是指兩個或多個文字之間的相似程度,通常使用向量空間模型(Vector Space Model)來表示文字。每個文字被轉換為一個向量,然後使用向量運算來計算文字之間的相似度。

Chroma 查詢與向量相似度搜尋

Chroma 是一個高效的向量查詢引擎,允許我們對大量的文字向量進行快速查詢。以下是 Chroma 查詢的示例:

import time
start_time = time.time()

results = collection.query(
    query_texts=df["question"][:nb],
    n_results=1
)
response_time = time.time() - start_time
print(f"Response Time: {response_time:.2f} seconds")

這個查詢會對 df["question"] 中的每個問題文字進行向量相似度搜尋,並傳回最相似的文字。

使用 spaCy 進行文字相似度評估

spaCy 是一個現代的自然語言處理(NLP)函式庫,提供了高效的文字處理和分析功能。以下是使用 spaCy 進行文字相似度評估的示例:

import spacy
import numpy as np

nlp = spacy.load('en_core_web_md')

def simple_text_similarity(text1, text2):
    doc1 = nlp(text1)
    doc2 = nlp(text2)
    vector1 = doc1.vector
    vector2 = doc2.vector
    similarity = np.dot(vector1, vector2) / (np.linalg.norm(vector1) * np.linalg.norm(vector2))
    return similarity

這個函式會將兩個文字轉換為 spaCy 檔案物件,然後計算兩個檔案之間的向量相似度。

圖表翻譯:文字相似度分析流程

以下是文字相似度分析流程的 Mermaid 圖表:

  flowchart TD
    A[問題文字] --> B[Chroma 查詢]
    B --> C[向量相似度搜尋]
    C --> D[spaCy 文字相似度評估]
    D --> E[相似度結果]

這個圖表展示了從問題文字到最終相似度結果的分析流程。

內容解密:向量相似度搜尋

向量相似度搜尋是一種常見的文字相似度分析方法。它將每個文字轉換為一個向量,然後使用向量運算來計算文字之間的相似度。Chroma 查詢就是使用這種方法來進行向量相似度搜尋的。

圖表翻譯:向量相似度搜尋流程

以下是向量相似度搜尋流程的 Mermaid 圖表:

  flowchart TD
    A[文字集合] --> B[向量轉換]
    B --> C[向量索引]
    C --> D[查詢向量]
    D --> E[向量相似度計算]
    E --> F[結果傳回]

這個圖表展示了從文字集合到最終結果傳回的向量相似度搜尋流程。

驗證程式與準確率評估

在進行全面的驗證過程中,我們會對10,000個查詢進行分析。這個過程由玄貓開始,設定只顯示前100和後100個結果。為了評估準確率,我們使用acc_counter來計算相似度分數大於0.5的結果,你可以根據需要調整這個門檻值。另外,display_counter用於計數我們顯示的結果數量。

驗證過程實作

nbqd = 100  # 顯示的回應數量
acc_counter = 0  # 準確率計數器
display_counter = 0  # 顯示計數器

for i, q in enumerate(df['question'][:nb]):
    original_completion = df['completion'][i]  # 取得原始完成
    retrieved_document = results['documents'][i][0]  # 取得檢索檔案
    similarity_score = simple_text_similarity(original_completion, retrieved_document)  # 計算相似度
    
    if similarity_score > 0.7:  # 準確率門檻值
        acc_counter += 1
    
    display_counter += 1
    if display_counter <= nbqd or display_counter > nb - nbqd:
        print(i, " ", f"問題:{q}")
        print(f"檢索檔案:{retrieved_document}")
        print(f"原始完成:{original_completion}")
        print(f"相似度分數:{similarity_score:.2f}")
        print()  # 空行以提高可讀性

輸出與分析

輸出的資訊包括問題、檢索檔案、原始完成和相似度分數。這些資訊允許人類觀察者控制查詢結果並追蹤其源頭。

  1. 問題:被問的問題,即查詢。
  2. 檢索檔案:根據查詢檢索到的檔案。
  3. 原始完成:資料集中原始的完成文字。
  4. 相似度分數:原始文字和檢索檔案之間的相似度分數,用於衡量每個回應的效能。

準確率計算

當所有結果被分析後,程式計算了10,000多個查詢的準確率。這個準確率反映了系統在找到相關且準確的檔案方面的效能。透過調整相似度門檻值,可以根據不同的需求定製準確率評估標準。

進一步最佳化查詢系統的效能和穩定性

在上一節中,我們已經實作了基本的查詢系統,並且對其進行了測試。然而,為了使系統更加穩定和高效,我們需要進一步最佳化它。

最佳化查詢過程

首先,我們需要最佳化查詢過程,以減少查詢時間和提高系統的回應速度。為此,我們可以使用以下幾種方法:

  • 使用索引: 我們可以對資料函式庫中的資料建立索引,以加速查詢過程。
  • 最佳化查詢陳述式: 我們可以最佳化查詢陳述式,以減少查詢的複雜度和提高查詢速度。
  • 使用快取: 我們可以使用快取機制,將常用的查詢結果暫存起來,以減少查詢次數和提高查詢速度。

測試查詢系統

接下來,我們需要測試查詢系統,以確保其效能和穩定性。為此,我們可以使用以下幾種方法:

  • 單元測試: 我們可以對查詢系統的每個模組進行單元測試,以確保其功能正確。
  • 整合測試: 我們可以對查詢系統的整個流程進行整合測試,以確保其效能和穩定性。
  • 壓力測試: 我們可以對查詢系統進行壓力測試,以確保其在高負載下的效能和穩定性。

最佳化查詢結果

最後,我們需要最佳化查詢結果,以提高其相關性和精確性。為此,我們可以使用以下幾種方法:

  • 使用排名演算法: 我們可以使用排名演算法,對查詢結果進行排名,以提高其相關性和精確性。
  • 使用過濾機制: 我們可以使用過濾機制,對查詢結果進行過濾,以去除無關的結果。
  • 使用聚類別演算法: 我們可以使用聚類別演算法,對查詢結果進行聚類別,以提高其相關性和精確性。

內容解密:

import time
import textwrap

# 啟動計時器
start_time = time.time()

# 查詢系統的主要邏輯
def query_system(prompt):
    # 對資料函式庫中的資料進行索引
    index = create_index()
    
    # 最佳化查詢陳述式
    query = optimize_query(prompt)
    
    # 使用快取機制
    cache = use_cache(query)
    
    # 對查詢結果進行排名
    ranked_results = rank_results(cache)
    
    # 對查詢結果進行過濾
    filtered_results = filter_results(ranked_results)
    
    # 對查詢結果進行聚類別
    clustered_results = cluster_results(filtered_results)
    
    return clustered_results

# 測試查詢系統
def test_query_system():
    # 單元測試
    unit_test()
    
    # 整合測試
    integration_test()
    
    # 壓力測試
    stress_test()

# 執行查詢系統
results = query_system("Millions of years ago, plants used energy from the sun")
print(results)

# 結束計時器
end_time = time.time()
print(f"查詢時間:{end_time - start_time} 秒")

圖表翻譯:

  graph LR
    A[啟動計時器] --> B[查詢系統的主要邏輯]
    B --> C[對資料函式庫中的資料進行索引]
    C --> D[最佳化查詢陳述式]
    D --> E[使用快取機制]
    E --> F[對查詢結果進行排名]
    F --> G[對查詢結果進行過濾]
    G --> H[對查詢結果進行聚類別]
    H --> I[傳回查詢結果]
    I --> J[結束計時器]

查詢集合使用提示

結果 = 集合查詢( 查詢文字 = [提示],# 使用預期的提示清單 n_results = 1 # 要檢索的結果數 )

測量回應時間

回應時間 = 時間.時間() - 啟動時間

列印回應時間

列印(f"回應時間:{回應時間:.2f} 秒\n")

檢查是否檢索到檔案

如果 結果[‘檔案’] 且 len(結果[‘檔案’][0]) > 0: # 使用 textwrap 格式化輸出以提高可讀性 包裝問題 = textwrap.fill(提示,寬度 = 70) # 包裝問題 包裝檔案 = textwrap.fill(結果[‘檔案’][0][0],寬度 = 70) # 包裝檔案

# 列印格式化結果
列印(f"問題:{包裝問題}")

列印("\n")

列印(f"檢索到的檔案:{包裝檔案}")

列印()

否則: 列印(“沒有檢索到檔案。”)

回應時間很快: 回應時間:0.03 秒 輸出顯示檢索到的檔案相關: 回應時間:0.03 秒 問題:數百萬年前,植物使用太陽能量 檢索到的檔案:葉綠體因為古代植物未經處理 它們將陽光中的能量轉化為儲存的化學能量中的食物 植物使用食物,食物也被吃掉植物的生物使用 在植物和其他生物死亡後,它們的殘骸逐漸地變成 化石燃料,因為它們被覆寫和壓縮。 我們已經成功地檢索了查詢的結果。這個語義向量搜尋已經取得了良好的效果。

圖表翻譯:

  graph LR
    A[查詢集合] --> B[檢索檔案]
    B --> C[測量回應時間]
    C --> D[列印回應時間]
    D --> E[檢查檔案]
    E --> F[列印結果]

圖表解釋:

此圖表描述了查詢集合的過程。首先,查詢集合(A)會檢索檔案(B),然後測量回應時間(C)並列印回應時間(D)。接下來,會檢查是否檢索到檔案(E),如果有,則列印結果(F)。如果沒有檢索到檔案,則不列印任何結果。

使用RAG與Llama進行自然語言處理

RAG(Retrieval-Augmented Generation)是一種結合檢索和生成的自然語言處理技術,能夠有效地提高生成文字的品質和相關性。當與Llama(一種大語言模型)結合使用時,RAG可以根據輸入的提示生成高品質的文字。

組態Llama 2的行為

為了使用RAG與Llama,需要先組態Llama 2的行為。這可以透過定義一個函式來實作,該函式接受一個提示作為輸入,並傳回生成的文字。以下是組態Llama 2行為的示例程式碼:

def LLaMA2(prompt):
    sequences = pipeline(
        prompt,
        do_sample=True,
        top_k=10,
        num_return_sequences=1,
        eos_token_id=tokenizer.eos_token_id,
        max_new_tokens=100,
        temperature=0.5,
        repetition_penalty=2.0,
        truncation=True
    )
    return sequences

在這個程式碼中,pipeline是一個用於生成文字的函式,接受一個提示和一系列引數作為輸入。這些引數包括:

  • prompt: 輸入的提示文字
  • do_sample: 一個布林值,當設定為True時,啟用隨機取樣
  • top_k: 一個整數,限制了在取樣過程中考慮的最高機率語彙令牌數量
  • num_return_sequences: 一個整數,指定了傳回的獨立生成回應數量
  • eos_token_id: 一個令牌,標誌著序列的結束
  • max_new_tokens: 一個整數,限制了模型可以生成的新令牌數量
  • temperature: 一個浮點數,控制了取樣過程中的隨機性
  • repetition_penalty: 一個浮點數,修改了模型重複使用相同令牌的可能性

將結果追加到提示中

在生成文字之前,需要將結果追加到提示中。這可以透過以下程式碼實作:

iprompt = 'Read the following input and write a summary for beginners'
lprompt = iprompt + " " + results['documents'][0][0]

在這個程式碼中,iprompt是原始的提示文字,lprompt是追加了結果的新的提示文字。

測量回應時間

為了測量Llama回應的時間,可以使用以下程式碼:

import time
start_time = time.time()
response = LLaMA2(lprompt)
response_time = time.time() - start_time
print(f"Response Time: {response_time:.2f} seconds")

在這個程式碼中,start_time是開始時間,response_time是回應時間。

包裝回應

最後,可以使用以下程式碼包裝回應:

wrapped_response = textwrap.fill(response[0]['generated_text'], 80)
print(wrapped_response)

在這個程式碼中,wrapped_response是包裝後的回應文字,80是包裝後的行寬。

圖表翻譯:

  flowchart TD
    A[開始] --> B[組態Llama 2]
    B --> C[生成文字]
    C --> D[追加結果到提示中]
    D --> E[測量回應時間]
    E --> F[包裝回應]
    F --> G[輸出結果]

在這個圖表中,描述了使用RAG與Llama進行自然語言處理的流程。

化石燃料的形成與組成

化石燃料是由古代植物和動物的殘骸經過數百萬年的地質過程形成的。這些植物和動物在生長過程中,透過光合作用將陽光中的能量轉化為化學能量,並儲存在其組織中。當這些植物和動物死亡後,其殘骸被埋葬在沉積巖中,並在高壓和高溫下逐漸轉化為化石燃料。

石油和天然氣的形成

石油和天然氣主要是由海洋生物的殘骸形成的。這些生物在死亡後,沉積在海底,並被覆寫著沉積物。隨著時間的推移,這些殘骸被壓縮和加熱,形成了石油和天然氣。石油主要由碳氫化合物組成,而天然氣則主要由甲烷(CH4)組成。

煤炭的形成

煤炭主要是由古代植物的殘骸形成的,特別是巨型蕨類別植物和其他沼澤植物。這些植物在死亡後,沉積在沼澤中,並被覆寫著沉積物。隨著時間的推移,這些殘骸被壓縮和加熱,形成了煤炭。

化石燃料的組成

化石燃料的組成複雜多樣,但主要包括碳氫化合物、甲烷(CH4)、乙烯(C2H6)、丙烯(C3H6)等。其中,石油和天然氣是最常見的化石燃料,廣泛用於能源生產和工業製造。

動態RAG過程

  1. 環境設定:安裝和分析環境,下載和準備SciQ資料集,以模擬每日會議中提出艱難科學問題的情況。
  2. Chroma集合向量儲存:建立Chroma集合向量儲存,並嵌入10,000+檔案,插入資料和向量到Chroma向量儲存中。
  3. 查詢集合:查詢集合以測量系統的準確性,結果令人滿意,因此處理完整資料集以確認。
  4. 使用者提示和查詢功能:建立使用者提示和查詢功能,以實作實時查詢和增強使用者輸入。

優點和限制

動態RAG過程提供了一種開源、輕量級、RAG驅動的生成AI方法,實作快速資料收集、嵌入和查詢。然而,這個過程需要更多工作才能投入生產使用。

未來發展

如果需要儲存檢索資料並不想建立大型向量儲存,可以透過微調整合資料集到OpenAI GPT-4o-mini模型中。

問題解答

  1. 是否確保Hugging Face API令牌不直接硬編碼到筆記本中:是
  2. 是否使用accelerate函式庫來促進ML模型在雲平臺上的佈署:否
  3. 是否需要使用者身份驗證與API令牌分開來存取Chroma資料函式庫:否
  4. 是否使用Chroma進行動態檢索過程中的臨時向量儲存:是
  5. 是否組態筆記本使用GPU最佳化來實作查詢的實時加速:否
  6. 是否可以使用筆記本的會話時間測量來最佳化動態RAG過程:是
  7. 是否展示Chroma與ML模型整合以增強檢索效能的能力:是
  8. 是否包含調整Chroma資料函式庫引數根據會話效能指標的功能:否

第九章:啟動AI模型:微調RAG資料和人類反饋

在本章中,我們將探討微調RAG資料的架構,關注於包含人類反饋的資料集,並展示如何將非引數資料轉換為引數化、微調的資料。首先,我們將從前一章下載和準備資料集,將其轉換為適合微調的JSONL格式。然後,我們將微調一個成本有效的OpenAI模型GPT-4o-mini,並測試它以驗證其是否成功地納入了我們的資料。最後,我們將探索OpenAI的度量介面,以評估我們方法的成本效益。

微調RAG資料的限制

當RAG資料的體積超過可管理的閾值時,會出現儲存成本、檢索資源和生成AI模型能力等問題。預訓練的生成AI模型僅在截止日期前有效,無法處理新知識。因此,檢索在提供RAG驅動內容方面發揮著關鍵作用。

微調架構

在這一節中,我們將質疑非引數化RAG資料的使用當它超過可管理的閾值。圖9.1描述了這一原則:

圖9.1:RAG資料微調閾值

請注意,處理(D2)和儲存(D3)閾值已經達到靜態資料與動態資料之間的RAG資料環境。閾值取決於每個專案和引數,如資料體積、儲存和檢索資源等。

RAG生態系統

在這一節中,我們將重溫第1章中描述的RAG生態系統,並關注本章所需的特定元件。圖9.2展示了微調元件:

圖9.2:RAG生態系統中的微調元件

微調生態系統的關鍵特徵包括:

  • 收集(D1)和準備(D2)資料集
  • 人類反饋(E2)
  • 微調(T2)
  • 提示工程(G3)和生成和輸出(G4)
  • 度量(E1)

安裝環境

開啟Fine_tuning_OpenAI_GPT_4o_mini.ipynb筆記本,並從GitHub下載OpenAI API金鑰。

#您可以從檔案中檢索API金鑰

微調OpenAI模型

微調一個成本有效的OpenAI模型GPT-4o-mini,並測試它以驗證其是否成功地納入了我們的資料。

  flowchart TD
    A[開始] --> B[下載資料集]
    B --> C[轉換為JSONL格式]
    C --> D[微調OpenAI模型]
    D --> E[測試模型]
    E --> F[評估成本效益]

內容解密:

  • 下載資料集:從前一章下載SciQ硬科學資料集。
  • 轉換為JSONL格式:將資料集轉換為適合微調的JSONL格式。
  • 微調OpenAI模型:微調一個成本有效的OpenAI模型GPT-4o-mini。
  • 測試模型:測試模型以驗證其是否成功地納入了我們的資料。
  • 評估成本效益:評估我們方法的成本效益。

圖表翻譯:

圖9.1描述了RAG資料微調閾值,圖9.2展示了RAG生態系統中的微調元件。Mermaid流程圖展示了微調OpenAI模型的流程。

  flowchart TD
    A[開始] --> B[下載資料集]
    B --> C[轉換為JSONL格式]
    C --> D[微調OpenAI模型]
    D --> E[測試模型]
    E --> F[評估成本效益]

準備資料集以進行微調

微調 OpenAI 模型需要仔細的資料準備,否則微調任務將會失敗。在本文中,我們將進行以下步驟:

  1. 從 Hugging Face 下載資料集並由玄貓進行準備。
  2. 將資料集轉換為 JSONL 格式並儲存到檔案中。

首先,我們需要安裝必要的函式庫,包括 openaijsonlinesdatasets。我們可以使用 pip 來安裝這些函式庫:

!pip install openai==1.42.0
!pip install jsonlines==4.0.0
!pip install datasets==2.20.0

接下來,我們需要設定 OpenAI API 金鑰。您可以從檔案中讀取金鑰或手動輸入:

import os

# 從檔案中讀取 API 金鑰
f = open("drive/MyDrive/files/api_key.txt", "r")
API_KEY = f.readline()
f.close()

# 或手動輸入 API 金鑰
# API_KEY = "您的 API 金鑰"

os.environ['OPENAI_API_KEY'] = API_KEY
openai.api_key = os.getenv("OPENAI_API_KEY")

現在,我們可以開始準備資料集了。首先,我們需要下載資料集:

import datasets

# 下載資料集
dataset = datasets.load_dataset("您的資料集名稱")

接下來,我們需要將資料集轉換為 JSONL 格式並儲存到檔案中:

import jsonlines

# 開啟檔案以進行寫入
with jsonlines.open("資料集檔案.jsonl", mode="w") as writer:
    # 將資料集轉換為 JSONL 格式並寫入檔案
    for example in dataset:
        writer.write(example)

完成這些步驟後,我們就可以使用準備好的資料集進行微調了。

內容解密:

在上述程式碼中,我們使用 openai 函式庫來設定 OpenAI API 金鑰,然後使用 datasets 函式庫來下載資料集。接下來,我們使用 jsonlines 函式庫來將資料集轉換為 JSONL 格式並儲存到檔案中。這些步驟是微調 OpenAI 模型的必要準備工作。

圖表翻譯:

  flowchart TD
    A[開始] --> B[下載資料集]
    B --> C[轉換為 JSONL 格式]
    C --> D[儲存到檔案]
    D --> E[完成]

在這個流程圖中,我們可以看到準備資料集的步驟:下載資料集、轉換為 JSONL 格式、儲存到檔案,最後完成準備工作。

下載和視覺化資料集

在本文中,我們將下載 SciQ 資料集,並與第 8 章中相同的 Hugging Face 資料集進行比較。與第 8 章不同的是,這次我們不會嵌入資料集,而是讓 OpenAI 模型在微調資料的過程中處理嵌入。

結論:動態 RAG 與微調模型的平衡之道

從技術架構視角來看,本文探討了動態 RAG 系統的建構,涵蓋資料載入、回應時間測量、向量嵌入顯示與查詢等關鍵環節。透過 Python 程式碼範例及 Mermaid 圖表,深入淺出地闡述了 Chroma 向量資料函式庫的運用,以及如何結合 spaCy 進行文字相似度分析與評估,展現了向量相似度搜尋在動態 RAG 中的核心地位。然而,動態 RAG 的效能瓶頸在於資料規模擴張時,儲存成本與檢索效率的下降。

本文進一步分析了動態 RAG 與模型微調的權衡。當資料量超越特定閾值時,微調小型語言模型,例如 GPT-4o-mini,成為更具成本效益的方案。此策略將非引數化的 RAG 資料轉化為引數化的微調資料,解決了動態 RAG 的 scalability 限制。同時,本文也強調了人類反饋在微調過程中的重要性,以及透過 OpenAI 度量介面評估成本效益的必要性。

展望未來,隨著大語言模型的持續發展,預期將出現更輕量化的微調方案,降低開發者整合特定領域知識的門檻。同時,向量資料函式庫技術的演進也將持續提升動態 RAG 的效能。玄貓認為,結合動態 RAG 與模型微調,將成為構建高效能、低成本知識密集型應用程式的主流趨勢。對於資源有限的團隊,優先將微調策略應用於核心業務場景,將能最大化商業價值。