LangChain for LLM Application Development 系列課程筆記
這部分的課程同樣滿缺乏組織性的,以下內容根據官方文件重新架構進行補充
說明大型語言模型如何通過嵌入向量(Embedding)理解不同文本的內容,即使在處理文本量上有所限制
LLM 處理文本的限制
目前的LLM一次只能處理數千個單詞(token)
Embedding的實際應用示例:
創建 (Create)
索引 (Index)
本節顯示了如何使用向量嵌入技術在大量文本數據中迅速查找和檢索相關信息。這在自然語言處理和機器學習中是一個常見的方法,尤其是當數據庫中有大量文本數據,並希望快速找到與特定查詢最相關的文本時
範例中所用的RetrievalQA
其實是放在"檢索增強生成"Retrieval-augmented generation (RAG)
的類別下
補充一下官網給的Use case範例圖
LLM資料檢索增強生成Retrieval-augmented generation(RAG)的流程
流程:處理非結構化數據並將其轉換為問答(QA)格式
Document Loading (文檔加載):
Splitting (分割):
Storage (存儲):
Retrieval (檢索):
Output/Generation (輸出/生成):
ps: 這個流程圖缺乏"增強"的部分
BaseCombineDocumentsChain
類別Chain
實作為一個通用介面( common interface)
class BaseCombineDocumentsChain(Chain, ABC):
"""Base interface for chains combining documents."""
@abstractmethod
def combine_docs(self, docs: List[Document], **kwargs: Any) -> Tuple[str, dict]:
"""Combine documents into a single string."""
方法 | 描述 | 優點 | 缺點 |
---|---|---|---|
Stuff | 將所有數據放入一個提示中,發送給語言模型並獲得單一回應。 | 簡單實用,適用於小文件。 | 不適用於大型或多個文件,因為會超出語言模型的上下文長度限制。 |
Map_reduce | 將所有文件與問題一起傳遞給語言模型,獲得回應後再用另一個語言模型呼叫來匯總各個回應。 | 功能強大,可平行處理問題。 | 需要更多語言模型呼叫,並將所有文檔視為獨立。 |
Refine | 在多個文檔間進行迭代處理,每一步建立在前一個文檔的答案上。 | 適合組合信息,隨時間構建答案。 | 處理速度較慢,因為呼叫之間不獨立,且答案通常較長。 |
Map_rerank | 對每個文檔進行單獨呼叫,要求語言模型返回一個評分,然後選擇最高分。 | 呼叫獨立,可以批量處理,相對快速。 | 依賴語言模型正確評分,需要多次語言模型呼叫,成本較高。 |
Stuff 方法是處理文件的最直接方式。它將一系列文件填充(stuff)到一個提示中,然後將這個提示傳遞給大型語言模型(LLM)
大量文件的問答見下三種方法簡介
Refine 方法通過循環處理輸入文件並迭代更新其答案來構建響應。對於每個文件,它將所有非文件輸入、當前文件和最新的中間答案傳遞給 LLM 鏈以獲得新答案
見Large Language Models with Semantic Search-ReRank。重新排序這邊也有介紹到
Map_rerank 方法在每個文件上運行一個初始提示,不僅嘗試完成任務,還會給出其答案確定性的評分。返回得分最高的回應
接下來看看程式碼實作部分,主要demo了 Index 與RetrievalQA(使用stuff方法)兩種檢索文件資料的方式
使用檢索問答鏈處理 RetrievalQA |
使用索引直接處理查詢 Index |
|
---|---|---|
過程描述 | 1. 查詢經過檢索器找到相關文件 2. 文件被整合成提示送給 LLM 3. LLM 生成回答 |
1. 查詢直接送到索引 2. 索引找到相關文件 3. 文件被送給 LLM 處理 |
適用情況 | 適合複雜查詢,需要綜合多個文件信息 | 適合快速、直接從文件集檢索信息,當查詢答案直接且明確 |
特點和優勢 | 結合多個文件的信息提供全面回答 適合於涉及大量文件的複雜情境 |
直接從特定文件集中檢索相關文件 適用於查詢答案可直接從特定文件獲取的情況 |
本節程式碼流程圖
index
方法進行查詢案例:如何將有興趣的商品向量化對目錄進行檢索
環境設定
import os
: 導入 os
模塊,通常用於與操作系統交互。from dotenv import load_dotenv, find_dotenv
: 從 dotenv
包導入 load_dotenv
和 find_dotenv
函數。這個包用於從 .env
文件讀取環境變數。_ = load_dotenv(find_dotenv())
: 使用 find_dotenv()
函數找到 .env
文件的路徑,然後使用 load_dotenv()
函數加載該文件中的環境變數。_
表示忽略返回值。import os
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
from langchain.document_loaders import CSVLoader
from langchain.vectorstores import DocArrayInMemorySearch
from IPython.display import display, Markdown
指定LLM
llm_model
變數設置為 "gpt-3.5-turbo"
。否則,設置為 "gpt-3.5-turbo-0301"
。這表明代碼作者預計在特定日期後會有一個新版本的語言模型可用
import datetime
current_date = datetime.datetime.now().date()
target_date = datetime.date(2024, 6, 12)
if current_date > target_date:
llm_model = "gpt-3.5-turbo"
else:
llm_model = "gpt-3.5-turbo-0301"
建立檢索問答系統加載並索引數據
創建了一個基於 DocArrayInMemorySearch
的索引,用於存儲和檢索從 CSV 文件加載的文件數據。這樣的索引使得文件的檢索變得快速和高效,特別適合於需要從大量數據中快速找到相關信息的應用。
file = 'OutdoorClothingCatalog_1000.csv'
loader = CSVLoader(file_path=file)
index = VectorstoreIndexCreator(
vectorstore_cls=DocArrayInMemorySearch
).from_loaders([loader])
VectorstoreIndexCreator
DocArrayInMemorySearch
)DocArrayInMemorySearch
是一種用於在記憶體(內存)中建立並管理文件集合的工具,通常用於快速檢索和處理文件 ,主要功能包括:
DocArrayInMemorySearch
使用場景
發送查詢並獲得回應
query = "Please list all your shirts with sun protection \
in a table in markdown and summarize each one."
response = index.query(query)
加載文件
CSVLoader
from langchain.document_loaders import CSVLoader
loader = CSVLoader(file_path=file)
docs = loader.load()
docs[0]
建立Embeddings
OpenAIEmbeddings
生成文件的語義Embeddings。這些Embeddings用於捕捉文件的意義,並在後續的搜索中使用
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
embed = embeddings.embed_query("Hi my name is Harrison")
使用 DocArrayInMemorySearch
和前面創建的嵌入建立一個文件搜索系統。這允許對文件進行相似性搜索。
db = DocArrayInMemorySearch.from_documents(docs, embeddings)
建立問答系統
query = "Please suggest a shirt with sunblocking"
docs = db.similarity_search(query)
ChatOpenAI
語言模型生成自然語言回答
llm = ChatOpenAI(temperature = 0.0, model=llm_model)
qdocs = "".join([docs[i].page_content for i in range(len(docs))])
response = llm.call_as_llm(
f"{qdocs} Question: Please list all your shirts with sun protection in a table in markdown and summarize each one.")
以下程式碼碼展示如何整合文件檢索和語言模型的能力,建立一個能夠針對特定查詢提供精確回答的系統
query = "Please list all your shirts with sun protection in a table \
in markdown and summarize each one."
retriever = db.as_retriever()
qa_stuff = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
verbose=True)
response = qa_stuff.run(query)
DocArrayInMemorySearch
的實例,轉換成一個檢索器(retriever = db.as_retriever()
)RetrievalQA.from_chain_type
方法建立了檢索問答鏈。這個方法需要幾個參數:
llm
:
chain_type
:指定問答鏈的類型。在這裡使用的stuff類型意味著將所有相關文件「塞入」(stuff)到一個單一的提示中,然後一次性送到語言模型進行處理retriever
:這就是前面創建的檢索器,它用於根據用戶的查詢來獲取相關文件qa_stuff
執行查詢。這個 run
方法接受一個查詢(query),然後執行以下流程:
結果比較
使用簡單索引(Index)查詢得到的結果
index = VectorstoreIndexCreator(
vectorstore_cls=DocArrayInMemorySearch,
embedding=embeddings).from_loaders([loader])
response = index.query(query, llm=llm)
display(Markdown(response))
結果說明
總結來說,兩個方法在描述相同的產品時,核心信息相同,但細節和陳述方式有所不同。
個人認為在這個案例中並未凸顯RetrievalQA的優勢,可能材料與使用的RGA方法都相對單純