一 準(zhǔn)備工作
下面是構(gòu)建這個(gè)應(yīng)用程序時(shí)將使用的軟件工具:
1.Llama-cpp-python
?下載llama-cpp, llama-cpp-python
[NLP] Llama2模型運(yùn)行在Mac機(jī)器-CSDN博客
2、LangChain
LangChain是一個(gè)提供了一組廣泛的集成和數(shù)據(jù)連接器,允許我們鏈接和編排不同的模塊??梢猿R娏奶鞕C(jī)器人、數(shù)據(jù)分析和文檔問答等應(yīng)用。
3、sentence-transformer
sentence-transformer提供了簡單的方法來計(jì)算句子、文本和圖像的嵌入。它能夠計(jì)算100多種語言的嵌入。我們將在這個(gè)項(xiàng)目中使用開源的all-MiniLM-L6-v2模型。
4、FAISS
Facebook AI相似度搜索(FAISS)是一個(gè)為高效相似度搜索和密集向量聚類而設(shè)計(jì)的庫。
給定一組嵌入,我們可以使用FAISS對它們進(jìn)行索引,然后利用其強(qiáng)大的語義搜索算法在索引中搜索最相似的向量。
雖然它不是傳統(tǒng)意義上的成熟的向量存儲(如數(shù)據(jù)庫管理系統(tǒng)),但它以一種優(yōu)化的方式處理向量的存儲,以實(shí)現(xiàn)有效的最近鄰搜索。
二 LLM應(yīng)用架構(gòu):以文檔Q&A為例
假設(shè)我們想基于自己部署的Llama2模型,構(gòu)建一個(gè)用于回答針對特定文檔內(nèi)容提問的聊天機(jī)器人。文檔的內(nèi)容可能屬于特定領(lǐng)域或者特定組織內(nèi)的文檔,因此不屬于任何Llama2進(jìn)行預(yù)訓(xùn)練和微調(diào)的數(shù)據(jù)集。一個(gè)直覺的做法是in-context-learning:將文檔作為Prompt提供給模型,從而模型能夠根據(jù)所提供的Context進(jìn)行回答。直接將文檔作為Context可能遇到的問題是:
- 文檔的長度超出了模型的Context長度限制,原版Llama2的Context長度為4096個(gè)Tokens。
- 對于較長的Context,模型可能會Lost in the Middle,無法準(zhǔn)確從Context中獲取關(guān)鍵信息。
因此,我們希望在構(gòu)建Prompt時(shí),只輸入與用戶的問題最相關(guān)的文檔內(nèi)容。
以下是構(gòu)建文檔Q&A應(yīng)用的常用架構(gòu):
- 文檔處理與存儲:將原始文本進(jìn)行分塊 (Splitting),并使用語言模型對每塊文本進(jìn)行embedding,得到文本的向量表示,最后將文本向量存儲在支持相似性搜索的向量數(shù)據(jù)庫中。
- 用戶詢問和Prompt構(gòu)建:根據(jù)用戶輸入的詢問,使用相似性搜索在向量數(shù)據(jù)庫中提取出與詢問最相關(guān)的一些文檔分塊,并將用戶詢問+文檔一起構(gòu)建Prompt,隨后輸入LLM并得到回答。
三 實(shí)際使用
LangChain
在上節(jié)中描述了以文檔Q&A為例的LLM應(yīng)用Pipeline架構(gòu)。LangChain是構(gòu)建該類大模型應(yīng)用的框架,其提供了模塊化組件(例如上文圖中的Document loader, Text splitter, Vector storage)的抽象和實(shí)現(xiàn),并支持集成第三方的實(shí)現(xiàn)(例如可以使用不同第三方提供的Vector Storage服務(wù))。通過LangChain可以將大模型與自定義的數(shù)據(jù)源結(jié)合起來構(gòu)建Pipeline。
https://github.com/langchain-ai/langchain?github.com/langchain-ai/langchain
構(gòu)建步驟
我們已經(jīng)了解了各種組件,接下來讓逐步介紹如何構(gòu)建文檔問答應(yīng)用程序。
由于已經(jīng)有許多教程了,所以我們不會深入到復(fù)雜和一般的文檔問答組件的細(xì)節(jié)(例如,文本分塊,矢量存儲設(shè)置)。在本文中,我們將把重點(diǎn)放在開源LLM和CPU推理方面。
使用llama-cpp-python啟動llama2 api服務(wù)
python3 -m llama_cpp.server --model TheBloke--Chinese-Alpaca-2-7B-GGUF/chinese-alpaca-2-7b.Q4_K_M.gguf ? --n_gpu_layers 1
INFO: ? ? Started server process [63148]
INFO: ? ? Waiting for application startup.
INFO: ? ? Application startup complete.
INFO: ? ? Uvicorn running on http://localhost:8000 (Press CTRL+C to quit)
使用LangChain調(diào)用本地部署的Llama2
下面的示例將使用LangChain的API調(diào)用本地部署的Llama2模型。
from langchain.chat_models import ChatOpenAI
chat_model = ChatOpenAI(openai_api_key = "EMPTY", openai_api_base = "http://localhost:8000/v1", max_tokens=256)
- 由于本地部署的llama-cpp-python提供了類OpenAI的API,因此可以直接使用
ChatOpenAI
接口,這將調(diào)用/v1/chat/completions
?API。 - 由于并沒有使用真正的OpenAI API,因此
open_ai_key
可以任意提供。openai_pi_base
為模型API的Base URL。max_tokens
限制了模型回答的長度。
from langchain.chat_models import ChatOpenAI
chat_model = ChatOpenAI(openai_api_key = "EMPTY", openai_api_base = "http://localhost:8000/v1", max_tokens=256)
from langchain.schema import AIMessage, HumanMessage, SystemMessage
system_text = "You are a helpful assistant."
human_text1 = "What is the capital of France?"
assistant_text = "Paris."
human_text2 = "How about England?"
messages = [SystemMessage(content=system_text),
HumanMessage(content=human_text1),
AIMessage(content=assistant_text),
HumanMessage(content=human_text2)]
chat_model.predict_messages(messages)
四 文檔Q&A應(yīng)用
這里將演示如何使用LangChain構(gòu)建一個(gè)簡單的文檔Q&A應(yīng)用Pipeline:
- Document Loading?+?Text Splitting
- Text Embeddings + Vector Storage
- Text Retrieval? +?Query LLM
1 數(shù)據(jù)加載與處理?Document Loading?+?Text Splitting
本實(shí)驗(yàn)使用llama.cpp的README.md作為我們需要進(jìn)行詢問的文檔。LangChain提供了一系列不同格式文檔的Loader,包括用于加載Markdown文檔的UnstructuredMarkdownLoader
:
UnstructuredMarkdownLoader
默認(rèn)會將文檔中的不同Elements(各級標(biāo)題,正文等)組合起來,去掉了#
等字符。
RecursiveCharacterTextSplitter
是對常見文本進(jìn)行Split的推薦選擇:
RecursiveCharacterTextSplitter
遞歸地在文本中尋找能夠進(jìn)行分割的字符(默認(rèn)為["\n\n", "\n", " ", ""]
)。這將盡可能地保留完整的段落,句子和單詞。
-
chunk_size
: 文本進(jìn)行Split后每個(gè)分塊的最大長度,所有分塊將在這個(gè)限制以內(nèi) -
chunk_overlap
: 前后分塊overlap的長度,overlap是為了保持前后兩個(gè)分塊之間的語義連續(xù)性 -
length_function
: 度量文本長度的方法
from langchain.document_loaders import UnstructuredMarkdownLoader
loader = UnstructuredMarkdownLoader("./README.md")
text = loader.load()
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size = 2000,
chunk_overlap = 400,
length_function = len,
is_separator_regex = False
)
all_splits = text_splitter.split_documents(text)
2.矢量存儲 Text Embeddings + Vector Storage
這一步的任務(wù)是:將文本分割成塊,加載嵌入模型,然后通過FAISS 進(jìn)行向量的存儲
Vector Storage
這里以FAISS
向量數(shù)據(jù)庫作為示例,?FAISS
基于Facebook AI Similarity Search(Faiss)庫 。
pip install faiss-cpu
from langchain.embeddings import HuggingFaceEmbeddings
# Load embeddings model
embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2',
model_kwargs={'device': 'cpu'})
from langchain.vectorstores import FAISS
vectorstore = FAISS.from_documents(all_splits, embeddings)
vectorstore.save_local('vectorstore/db_faiss')
#question = "How to run the program in interactive mode?"
#docs = vectorstore.similarity_search(question, k=1)
運(yùn)行上面的Python腳本后,向量存儲將被生成并保存在名為'vectorstore/db_faiss'的本地目錄中,并為語義搜索和檢索做好準(zhǔn)備。
3.文本檢索與LLM查詢 Text Retrieval? +?Query LLM
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
chat_model = ChatOpenAI(openai_api_key = "EMPTY", openai_api_base = "http://localhost:8000/v1", max_tokens=256)
qa_chain = RetrievalQA.from_chain_type(chat_model, retriever=vectorstore.as_retriever(search_kwargs={"k": 1}))
qa_chain({"query": "How to run the program in interactive mode?"})
構(gòu)造RetrievalQA
需要提供一個(gè)LLM的實(shí)例,我們提供基于本地部署的Llama2構(gòu)造的ChatOpenAI
;還需要提供一個(gè)文本的Retriever,我們提供FAISS
向量數(shù)據(jù)庫作為一個(gè)Retriever,參數(shù)search_kwargs={"k":1}
設(shè)置了Retriever提取的文檔分塊的數(shù)量,決定了最終Prompt包含的文檔內(nèi)容的數(shù)量,在這里我們設(shè)置為1。 向Chain中傳入詢問,即可得到LLM根據(jù)Retriever提取的文檔做出的回答。
自定義RetrievalQA
的Prompt:
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
template = """Use the following pieces of context to answer the question at the end.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
Use three sentences maximum and keep the answer as concise as possible.
Always say "thanks for asking!" at the end of the answer.
{context}
Question: {question}
Helpful Answer:"""
QA_CHAIN_PROMPT = PromptTemplate.from_template(template)
qa_chain = RetrievalQA.from_chain_type(
chat_model,
retriever=vectorstore.as_retriever(search_kwargs={"k": 1}),
chain_type_kwargs={"prompt": QA_CHAIN_PROMPT}
)
qa_chain({"query": "What is --interactive option used for?"})
from langchain.chains import LLMChain
from langchain_core.runnables import RunnablePassthrough
llm_chain = LLMChain(llm=chat, prompt=prompt)
query = "What is --interactive option used for?"
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| llm_chain
)
rag_chain.invoke(query)
結(jié)論
本文介紹了如何在MacBook Pro本地環(huán)境使用llama.cpp部署ggml量化格式的Llama2語言模型,并演示如何使用LangChain簡單構(gòu)造了一個(gè)文檔Q&A應(yīng)用。
References
[1]?https://github.com/ggerganov/llama.cpp
[2]?QA over Documents | ? Langchain
在MacBook Pro部署Llama2語言模型并基于LangChain構(gòu)建LLM應(yīng)用 - 知乎 (zhihu.com)文章來源:http://www.zghlxwxcb.cn/news/detail-765060.html
使用GGML和LangChain在CPU上運(yùn)行量化的llama2 - 知乎 (zhihu.com)文章來源地址http://www.zghlxwxcb.cn/news/detail-765060.html
到了這里,關(guān)于[NLP] 使用Llama.cpp和LangChain在CPU上使用大模型-RAG的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!