為什么我們需要LLMs
語言的進化至今已經(jīng)為人類帶來了令人難以置信的進步。它使我們能夠以我們今天所知的形式有效地共享知識和協(xié)作。因此,我們的大部分集體知識繼續(xù)通過無組織的書面文本保存和傳播。
過去二十年來為實現(xiàn)信息和流程數(shù)字化而采取的舉措往往側重于在關系數(shù)據(jù)庫中積累越來越多的數(shù)據(jù)。這種方法使傳統(tǒng)的分析機器學習算法能夠處理和理解我們的數(shù)據(jù)。
然而,盡管我們付出了巨大的努力以結構化的方式存儲越來越多的數(shù)據(jù),但我們仍然無法捕獲和處理我們的全部知識。公司中大約80%的數(shù)據(jù)是非結構化的,例如工作描述、簡歷、電子郵件、文本文檔、幻燈片、錄音、視頻和社交媒體。
GPT3.5的開發(fā)和進步標志著一個重要的里程碑,因為它使我們能夠有效地解釋和分析不同的數(shù)據(jù)集,無論其結構或缺乏結構。如今,我們擁有可以理解和生成各種形式內容的模型,包括文本、圖像和音頻文件。
那么我們如何利用他們的功能來滿足我們的需求和數(shù)據(jù)呢?
微調與上下文注入
一般來說,我們有兩種根本不同的方法來使大型語言模型能夠回答它們無法知道的問題:模型微調和上下文注入。
微調
微調是指使用額外的數(shù)據(jù)訓練現(xiàn)有的語言模型,以針對特定任務對其進行優(yōu)化。
不是從頭開始訓練語言模型,而是使用BERT或LLama等預訓練模型,然后通過添加用例特定的訓練數(shù)據(jù)來適應特定任務的需求。
斯坦福大學的一個團隊使用了LLMsLlama,并通過使用50,000個用戶/模型交互的示例對其進行了微調。結果是一個與用戶交互并回答查詢的聊天機器人。這一微調步驟改變了模型與最終用戶交互的方式。
→關于微調的誤解
PLLM(預訓練語言模型)的微調是針對特定任務調整模型的一種方法,但它并不能真正允許將自己的領域知識注入到模型中。這是因為模型已經(jīng)接受了大量通用語言數(shù)據(jù)的訓練,而特定領域數(shù)據(jù)通常不足以覆蓋模型已經(jīng)學到的內容。
因此,當微調模型時,它偶爾可能會提供正確的答案,但通常會失敗,因為它嚴重依賴于預訓練期間學到的信息,而這些信息可能不準確或與特定任務不相關。換句話說,微調有助于模型適應其通信方式,但不一定適應其通信內容。
這就是上下文注入發(fā)揮作用的地方。
上下文學習/上下文注入
當使用上下文注入時,我們不會修改LLM,我們專注于提示本身并將相關上下文注入到提示中。
所以我們需要思考如何為提示提供正確的信息。在下圖中,可以示意性地看到整個過程是如何工作的,這也是非結構化數(shù)據(jù)相似性檢索。我們需要一個能夠識別最相關數(shù)據(jù)的流程。為此,我們需要使計算機能夠相互比較文本片段。
這可以通過嵌入來完成。通過嵌入,我們將文本轉換為向量,從而允許我們在多維嵌入空間中表示文本??臻g上彼此距離較近的點通常用于相同的上下文中。為了防止這種相似性搜索永遠持續(xù)下去,我們將向量存儲在向量數(shù)據(jù)庫中并為它們建立索引。
微軟向我們展示了如何將其與BingChat配合使用。Bing將LLMs理解語言和上下文的能力與傳統(tǒng)網(wǎng)絡搜索的效率結合起來。
本文的目的是演示創(chuàng)建一個簡單的解決方案的過程,該解決方案使我們能夠分析我們自己的文本和文檔,然后將從中獲得的見解合并到我們的解決方案返回給用戶的答案中。我將描述實施端到端解決方案所需的所有步驟和組件。
那么我們如何利用LLM的能力來滿足我們的需求呢?讓我們一步一步地看一下。
分步教程——創(chuàng)建第一個LLMs應用程序
接下來,我們希望利用LLMs來回應有關我們個人數(shù)據(jù)的詢問。為了實現(xiàn)這一目標,我首先將我們的個人數(shù)據(jù)內容傳輸?shù)绞噶繑?shù)據(jù)庫中。這一步至關重要,因為它使我們能夠有效地搜索文本中的相關部分。我們將使用我們的數(shù)據(jù)中的信息和LLMs的能力來解釋文本以回答用戶的問題。
我們還可以根據(jù)我們提供的數(shù)據(jù)引導聊天機器人專門回答問題。這樣,我們可以確保聊天機器人始終專注于手頭的數(shù)據(jù)并提供準確且相關的響應。
為了實現(xiàn)我們的用例,我們將依賴Langchain。
什么是Langchain
“Langchain是一個用于開發(fā)由語言模型驅動的應用程序的框架。”
因此,Langchain是一個Python框架,旨在支持創(chuàng)建各種LLM應用程序,例如聊天機器人、摘要工具以及基本上任何您想要創(chuàng)建以利用LLM功能的工具。該庫結合了我們需要的各種組件。我們可以將這些組件連接到所謂的鏈中。
Langchain最重要的模塊包括:
模型:各種模型類型的接口
提示:提示管理、提示優(yōu)化、提示序列化
索引:文檔加載器、文本拆分器、矢量存儲—實現(xiàn)更快、更高效地訪問數(shù)據(jù)
鏈:鏈超越了單個LLM調用,它們允許我們設置調用序列
在下圖中,可以看到這些組件的作用。我們使用索引模塊中的文檔加載器和文本分割器加載和處理我們自己的非結構化數(shù)據(jù)。提示模塊允許我們將找到的內容注入到我們的提示模板中,最后,我們使用模型的模塊將提示發(fā)送到我們的模型。
5.代理人:代理人是使用LLMs來選擇要采取的行動的實體。采取行動后,他們觀察該行動的結果并重復該過程,直到任務完成。
我們在第一步中使用Langchain來加載文檔、分析它們并使其可有效搜索。在我們對文本進行索引后,識別與回答用戶問題相關的文本片段應該會變得更加有效。
我們簡單的申請所需要的當然是LLMs。我們將通過OpenAI API使用GPT3.5。然后我們需要一個向量存儲,向LLM提供數(shù)據(jù)。如果我們想對不同的查詢執(zhí)行不同的操作,需要一個令牌來決定每個查詢應該發(fā)生什么。
讓我們從頭開始。我們首先需要導入文檔。
下面介紹Langchain的LoaderModule包含哪些模塊來從不同來源加載不同類型的文檔。
1.加載文檔
Langchain能夠從各種來源加載大量文檔??梢栽贚angchain文檔中找到可能的文檔加載器列表。其中包括HTML頁面、S3存儲桶、PDF、Notion、GoogleDrive等的加載器。
對于我們的簡單示例,我們使用的數(shù)據(jù)可能未包含在GPT3.5的訓練數(shù)據(jù)中。我使用有關GPT4的維基百科文章,因為我認為GPT3.5對GPT4的了解有限。
對于這個最小的例子,我沒有使用任何Langchain加載器,我只是使用Beautiful Soup直接從維基百科抓取文本。
請注意,抓取網(wǎng)站只能根據(jù)網(wǎng)站的使用條款以及您希望使用的文本和數(shù)據(jù)的版權/許可狀態(tài)進行。
importrequests
frombs4importBeautifulSoup
url="https://en.wikipedia.org/wiki/GPT-4"
response=requests.get(url)
soup=BeautifulSoup(response.content,'html.parser')
#找到contentdiv
content_div=soup.find('div',{'class':'mw-parser-output'})
#從div中刪除不需要的元素
unknown_tags=['sup','span','table','ul','ol']
for不需要的_tags中的標簽:
for匹配content_div.findAll(tag):
match.extract()
print(content_div.get_text())
2.將文檔分割成文本片段
接下來,必須將文本分成更小的部分,稱為文本塊。每個文本塊代表嵌入空間中的一個數(shù)據(jù)點,允許計算機確定這些塊之間的相似性。
以下文本片段利用了Langchain的文本分割器模塊。在這種特殊情況下,我們指定塊大小為100,塊重疊為20。通常使用較大的文本塊,但您可以進行一些試驗來找到適合用例的最佳大小。您只需要記住,每個LLM都有令牌限制(GPT3.5為4000個令牌)。由于我們要將文本塊插入提示中,因此需要確保整個提示不超過4000個標記。
fromLangchain.text_splitterimportRecursiveCharacterTextSplitterarticle_text
=content_div.get_text()
text_splitter=RecursiveCharacterTextSplitter(
#設置一個非常小的塊大小,只是為了顯示。
chunk_size=100,
chunk_overlap=20,
length_function=len,
)
texts=text_splitter.create_documents([article_text])
打印(文本[0])
打印(文本[1])
這將我們的整個文本分割如下:
3.從文本塊到嵌入
現(xiàn)在我們需要使文本組件易于理解并與我們的算法進行比較。我們必須找到一種方法將人類語言轉換為以位和字節(jié)表示的數(shù)字形式。
該圖像提供了一個對大多數(shù)人來說似乎顯而易見的簡單示例。然而,我們需要找到一種方法讓計算機理解“查爾斯”這個名字與男性而不是女性相關,如果查爾斯是男性,那么他就是國王而不是王后。
在過去的幾年里,出現(xiàn)了可以做到這一點的新方法和模型。我們想要的是一種能夠將單詞的含義翻譯成n維空間的方法,這樣我們就能夠相互比較文本塊,甚至計算它們相似性的度量。
嵌入模型試圖通過分析單詞通常使用的上下文來準確地學習這一點。由于茶、咖啡和早餐經(jīng)常在相同的上下文中使用,因此它們在n維空間中比茶和豌豆等彼此更接近。茶和豌豆聽起來很相似,但很少一起使用。
嵌入模型為我們提供了嵌入空間中每個單詞的向量。最后,通過使用向量表示它們,我們能夠執(zhí)行數(shù)學計算,例如計算單詞之間的相似度作為數(shù)據(jù)點之間的距離。
要將文本轉換為嵌入,有多種方法,例如Word2Vec、GloVe、fastText或ELMo。
嵌入模型
為了捕獲嵌入中單詞之間的相似性,Word2Vec使用簡單的神經(jīng)網(wǎng)絡??梢杂么罅课谋緮?shù)據(jù)訓練這個模型,并創(chuàng)建一個能夠將n維嵌入空間中的點分配給每個單詞的模型,從而以向量的形式描述其含義。
對于訓練,將輸入層中的神經(jīng)元分配給數(shù)據(jù)集中的每個唯一單詞。下圖中,可以看到一個簡單的示例。在這種情況下,隱藏層僅包含兩個神經(jīng)元。第二,因為我們想要將單詞映射到二維嵌入空間中。(現(xiàn)有模型實際上要大得多,因此表示更高維度空間中的單詞-例如OpenAI的Ada嵌入模型使用1536個維度)訓練過程之后,各個權重描述了嵌入空間中的位置。
在這個例子中,數(shù)據(jù)集由一個句子組成:“Google是一家科技公司?!本渥又械拿總€單詞都充當神經(jīng)網(wǎng)絡(NN)的輸入。因此,我們的網(wǎng)絡有五個輸入神經(jīng)元,每個單詞一個。
在訓練過程中,我們專注于預測每個輸入單詞的下一個單詞。當我們從句子開頭開始時,與單詞“Google”對應的輸入神經(jīng)元接收值1,而其余神經(jīng)元接收值0。我們的目標是訓練網(wǎng)絡來預測單詞“is”這個特定的場景。
實際上,學習嵌入模型的方法有多種,每種方法都有自己獨特的方式來預測訓練過程中的輸出。兩種常用的方法是CBOW(連續(xù)詞袋)和Skip-gram。
在CBOW中,我們將周圍的單詞作為輸入,旨在預測中間的單詞。相反,在Skip-gram中,我們將中間的單詞作為輸入,并嘗試預測出現(xiàn)在其左側和右側的單詞。不過,可以說,這些方法為我們提供了嵌入,它們是通過分析大量文本數(shù)據(jù)的上下文來捕獲單詞之間關系的表示。
返回嵌入模型
我剛剛嘗試使用二維嵌入空間中的簡單示例來解釋的內容也適用于更大的模型。例如,標準Word2Vec向量有300維,而OpenAI的Ada模型有1536維。這些預先訓練的向量使我們能夠精確地捕獲單詞及其含義之間的關系,以便我們可以用它們進行計算。例如,使用這些向量,我們可以發(fā)現(xiàn)法國+柏林-德國=巴黎,并且更快+溫暖-快速=更溫暖。
在下文中,我們不僅要使用OpenAIAPI來使用OpenAI的LLM,還要利用其嵌入模型。
注意:嵌入模型和LLMs之間的區(qū)別在于,嵌入模型專注于創(chuàng)建單詞或短語的向量表示以捕獲它們的含義和關系,而LLMs是經(jīng)過訓練的多功能模型,可根據(jù)提供的提示或查詢生成連貫且上下文相關的文本。
OpenAI嵌入模型
與OpenAI的各種LLM類似,您也可以在各種嵌入模型之間進行選擇,例如Ada、Davinci、Curie和Babbage。其中,Ada-002是目前速度最快、性價比最高的型號,而達芬奇一般提供最高的精度和性能。但是,您需要親自嘗試并找到適合您的用例的最佳模型。
我們嵌入模型的目標是將文本塊轉換為向量。對于第二代Ada,這些向量具有1536個輸出維度,這意味著它們表示1536維空間內的特定位置或方向。
OpenAI在其文檔中對這些嵌入向量的描述如下:
“數(shù)字上相似的嵌入在語義上也相似。例如,“caninecompaniessay”的嵌入向量將更類似于“woof”的嵌入向量,而不是“meow”的嵌入向量。
試一試吧。我們使用OpenAI的API將文本片段轉換為嵌入,如下所示:
導入openai
print(texts[0])
embedding=openai.Embedding.create(
input=texts[0].page_content,model="text-embedding-ada-002"
)["data"][0]["embedding"]
len(嵌入)
我們將文本(例如包含“2023文本生成語言模型”的第一個文本塊)轉換為1536維的向量。通過對每個文本塊執(zhí)行此操作,我們可以在1536維空間中觀察哪些文本塊彼此更接近且更相似。
試一試吧。我們的目標是通過生成問題的嵌入,然后將其與空間中的其他數(shù)據(jù)點進行比較,將用戶的問題與文本塊進行比較。
當我們將文本塊和用戶的問題表示為向量時,我們就獲得了探索各種數(shù)學可能性的能力。為了確定兩個數(shù)據(jù)點之間的相似性,我們需要計算它們在多維空間中的接近度,這是使用距離度量來實現(xiàn)的。有多種方法可用于計算點之間的距離。
常用的距離度量是余弦相似度。因此,讓我們嘗試計算問題和文本塊之間的余弦相似度:
importnumpyasnp
fromnumpy.linalgimportnorm
#計算用戶問題的嵌入
users_question="WhatisGPT-4?"
Question_embedding=get_embedding(text=users_question,model="text-embedding-ada-002")
#創(chuàng)建一個列表來存儲計算出的余弦相似度
cos_sim=[]
forindex,rowindf.iterrows():
A=row.ada_embedding
B=Question_embedding
#計算余弦相似度
cosine=np.dot(A,B)/(norm(A)*norm(B))
cos_sim.append(cosine)
df["cos_sim"]=cos_sim
df.sort_values(by=["cos_sim"],升序=False)
現(xiàn)在,我們可以選擇要提供給LLMs的文本塊數(shù)量,以回答問題。
下一步是確定我們想要使用哪個LLMs。
4.定義要使用的模型
Langchain提供了多種模型和集成,包括OpenAI的GPT和Huggingface等。如果我們決定使用OpenAI的GPT作為我們的大型語言模型,第一步就是定義我們的API密鑰。目前,OpenAI提供了一些免費使用容量,但是一旦我們每月超過一定數(shù)量的令牌,我們就需要切換到付費帳戶。
如果我們使用GPT來回答類似于如何使用Google的簡短問題,成本仍然相對較低。但是,如果我們使用GPT來回答需要提供廣泛上下文的問題,例如個人數(shù)據(jù),則查詢可以快速積累數(shù)千個令牌。這大大增加了成本。但不用擔心,可以設置成本限制。
什么是令牌?
簡單來說,令牌基本上是一個單詞或一組單詞。然而,在英語中,單詞可以有不同的形式,例如動詞時態(tài)、復數(shù)或復合詞。為了解決這個問題,我們可以使用子詞標記化,它將一個單詞分解成更小的部分,比如它的詞根、前綴、后綴和其他語言元素。例如,單詞“tiresome”可以分為“tire”和“some”,而“tired”可以分為“tire”和“d”。如此一來,我們就可以認識到“tiresome”和“tired”同根同源,有相似的派生關系。
OpenAI在其網(wǎng)站上提供了一個令牌生成器,以了解令牌是什么。根據(jù)OpenAI的說法,對于常見的英語文本,一個令牌通常對應于約4個字符的文本。這相當于大約3/4個單詞(因此100個標記~=75個單詞)。
設置使用限制
如果擔心費用,可以在OpenAI用戶門戶中找到一個選項來限制每月費用。
可以在OpenAI的用戶帳戶中找到API密鑰。最簡單的方法是在Google中搜索“OpenAI API密鑰”。這將直接帶您進入設置頁面,以創(chuàng)建新密鑰。
要在Python中使用,必須將密鑰保存為名稱為“OPENAI_API_KEY”的新環(huán)境變量:
導入os
os.environment["OPENAI_API_KEY"]="testapikey213412"
定義模型時,可以設置一些首選項。在決定要使用哪些設置之前,OpenAI Playground使您可以嘗試一下不同的參數(shù):
在PlaygroundWebUI的右側,會發(fā)現(xiàn)OpenAI提供的幾個參數(shù),使我們能夠影響LLM的輸出。值得探索的兩個參數(shù)是模型選擇和型號。
可以從各種不同的型號中進行選擇。Text-davinci-003模型是目前最大、最強大的。另一方面,像Text-ada-001這樣的型號更小、更快、更具成本效益。與最強大的模型Davinci相比,Ada更便宜。因此,如果Ada的性能滿足我們的需求,我們不僅可以節(jié)省資金,還可以實現(xiàn)更短的響應時間。
可以從使用Davinci開始,然后評估我們是否也可以使用Ada獲得足夠好的結果。
那么讓我們在JupyterNotebook中嘗試一下。我們正在使用Langchain連接到GPT。
從Langchain.llms導入OpenAI
llm=OpenAI(型號=0.7)
如果您想查看包含所有屬性的列表,請使用__dict__:
llm.__dict__
如果我們不指定特定模型,Langchain連接器默認使用“text-davinci-003”。
現(xiàn)在,我們可以直接在Python中調用該模型。只需調用llm函數(shù)并提供提示作為輸入即可。
現(xiàn)在可以向GPT詢問任何有關人類常識的問題。
GPT只能提供有關其訓練數(shù)據(jù)中未包含的主題的有限信息。這包括未公開的具體細節(jié)或上次更新訓練數(shù)據(jù)后發(fā)生的事件。
那么,我們如何確保模型能夠回答有關時事的問題?
正如前面提到的,有一種方法可以做到這一點。我們需要在提示中為模型提供必要的信息。
為了回答有關英國現(xiàn)任首相的問題,我向提示提供了維基百科文章“英國首相”中的信息。總結一下這個過程,我們是:
正在加載文章
將文本拆分為文本塊
計算文本塊的嵌入
計算所有文本塊與用戶問題之間的相似度
從bs4導入請求從Langchain.text_splitter導入BeautifulSoup導入RecursiveCharacterTextSplitter導入numpy作為np從numpy.linalg導入規(guī)范
##########################
加載文檔
###########################
要抓取的維基百科頁面的
URLurl='https://en.wikipedia.org/wiki/Prime_Minister_of_the_United_Kingdom'#向URL發(fā)送GET請求response=requests.get(網(wǎng)址)
#使用BeautifulSoup解析HTML內容
soup=BeautifulSoup(response.content,'html.parser')
#查找頁面上的所有文本
text=soup.get_text()
##############
分割文本
#############
text_splitter=RecursiveCharacterTextSplitter(
#設置一個非常小的塊大小,只是為了顯示。
chunk_size=100,
chunk_overlap=20,
length_function=len,
)texts
=text_splitter.create_documents([text])
用于文本文本:
text_chunks.append(text.page_content)
#####################
計算嵌入
#####################
df=pd.DataFrame({'text_chunks':text_chunks})
#創(chuàng)建包含所有文本塊的新列表
text_chunks=[]
fortextintexts:
text_chunks.append(text.page_content)
#從text-embedding-ada模型獲取嵌入
defget_embedding(文本,model="text-embedding-ada-002"):
text=text.replace("\n","")
returnopenai.Embedding.create(input=[text],model=model)['data'][0]['embedding']
df['ada_embedding']=df.text_chunks.apply(lambdax:get_embedding(x,模型='文本嵌入-ada-002'))
##################
計算與用戶問題的相似度
##################
計算用戶問題的嵌入
users_question="誰是現(xiàn)任英國首相?"
Question_embedding=get_embedding(text=users_question,“文本嵌入-ada-002”)
現(xiàn)在我們嘗試找到與用戶問題最相似的文本塊:
fromLangchainimportPromptTemplate
fromLangchain.llmsimportOpenAI
#計算用戶問題的嵌入
users_question="誰是現(xiàn)任英國首相?"
Question_embedding=get_embedding(text=users_question,model="text-embedding-ada-002")
#創(chuàng)建一個列表來存儲計算出的余弦相似度
cos_sim=[]
forindex,rowindf.iterrows():
A=row.ada_embedding
B=Question_embedding
#計算余弦相似度
cosine=np.dot(A,B)/(norm(A)*norm(B))
cos_sim.append(cosine)
df["cos_sim"]=cos_sim
df.sort_values(by=["cos_sim"],升序=False)
文本塊看起來相當混亂,但讓我們嘗試一下,看看GPT是否足夠聰明來處理它。
現(xiàn)在我們已經(jīng)確定了可能包含相關信息的文本片段,我們可以測試我們的模型是否能夠回答問題。為了實現(xiàn)這一目標,我們必須以一種能夠清楚地向模型傳達我們期望的任務的方式構建我們的提示。
5.定義提示模板
現(xiàn)在我們有了包含我們正在查找的信息的文本片段,我們需要構建一個提示。在提示中,我們還指定模型回答問題所需的模式。當我們定義模式時,我們指定了我們希望LLM生成答案的所需行為風格。
LLMs可用于各種任務,以下是各種可能性的一些示例:
總結:“將以下文本總結為3段,供高管使用:[TEXT]
知識提?。?/strong>“根據(jù)這篇文章:[TEXT],人們在購房之前應該考慮什么?”
編寫內容(例如郵件、消息、代碼):給Jane寫一封電子郵件,要求更新我們項目的文檔。使用非正式、友好的語氣?!?/p>
語法和風格改進:“將其更正為標準英語并將語氣更改為更友好的語氣:[TEXT]
分類:“將每條消息分類為一種支持票證類型:[TEXT]”
對于示例,我們希望實現(xiàn)一個從維基百科提取數(shù)據(jù)并像聊天機器人一樣與用戶交互的解決方案。我們希望它像積極主動、樂于助人的服務臺專家一樣回答問題。
為了引導LLM朝正確的方向發(fā)展,我在提示中添加以下說明:
“你是一個喜歡幫助別人的聊天機器人!僅使用所提供的上下文回答以下問題。如果您不確定并且上下文中沒有明確答案,請說“抱歉,我不知道如何幫助您。”
通過這樣做,我設置了一個限制,只允許GPT使用存儲在我們數(shù)據(jù)庫中的信息。此限制使我們能夠提供聊天機器人生成響應所依賴的來源,這對于可追溯性和建立信任至關重要。此外,它還幫助我們解決生成不可靠信息的問題,并使我們能夠提供可在企業(yè)環(huán)境中用于決策目的的答案。
作為上下文,我只是使用與問題相似度最高的前50個文本塊。文本塊的大小可能會更好,因為我們通??梢杂靡粌蓚€文本段落來回答大多數(shù)問題。您可以自行確定最適合的用例的尺寸。
fromLangchainimportPromptTemplate
fromLangchain.llmsimportOpenAI
#定義你想要使用的LLM
llm=OpenAI(Temperature=1)
#計算用戶問題的嵌入
users_question="WhoisthecurrentPrimeMinsteroftheUK?"
Question_embedding=get_embedding(text=users_question,model="text-embedding-ada-002")
#創(chuàng)建一個列表來存儲計算出的余弦相似度
cos_sim=[]
forindex,rowindf.iterrows():
A=row.ada_embedding
B=問題嵌入
#計算余弦相似度
cosine=np.dot(A,B)/(norm(A)*norm(B))
cos_sim.append(cosine)
df["cos_sim"]=cos_sim
df.sort_values(by=["cos_sim"],ascending=False)
#通過加入最相關的文本塊來定義提示的上下文
context=""
forindex,rowindf[0:50].iterrows():
context=context+""+row.text_chunks
#定義提示模板
template="""
你是一個喜歡幫助別人的聊天機器人!給出以下上下文部分,回答
僅使用給定上下文的問題。如果您不確定并且答案沒有
明確寫在文檔中,請說“抱歉,我不知道如何提供幫助?!?br>
上下文部分:
{context}
問題:
{users_question}
答案:
"""
PromptTemplate(template=template,input_variables=["context","users_question"])
#填寫提示模板
Prompt_text=Prompt.format(context=context,問題=用戶問題)
llm(提示文本)
通過使用該特定模板,我將上下文和用戶的問題合并到我們的提示中。結果響應如下:
令人驚訝的是,即使是這個簡單的實現(xiàn)似乎也產生了一些令人滿意的結果。讓我們繼續(xù)向系統(tǒng)詢問一些有關英國首相的問題。我將保持一切不變,僅替換用戶的問題:
users_question="誰是英國第一任首相?"
它似乎在某種程度上發(fā)揮了作用。然而,我們現(xiàn)在的目標是將這一緩慢的過程轉變?yōu)榉€(wěn)健且高效的過程。為了實現(xiàn)這一目標,我們引入了一個索引步驟,將嵌入和索引存儲在向量存儲中。這將提高整體性能并減少響應時間。
6.創(chuàng)建矢量存儲
矢量存儲是一種數(shù)據(jù)存儲類型,針對存儲和檢索可表示為矢量的大量數(shù)據(jù)進行了優(yōu)化。這些類型的數(shù)據(jù)庫允許基于各種標準(例如相似性度量或其他數(shù)學運算)有效地查詢和檢索數(shù)據(jù)子集。
將我們的文本數(shù)據(jù)轉換為向量是第一步,但這還不足以滿足我們的需求。如果我們將向量存儲在數(shù)據(jù)框中,并在每次收到查詢時逐步搜索單詞之間的相似性,那么整個過程將非常慢。
為了有效地搜索嵌入,我們需要對它們建立索引。索引是矢量數(shù)據(jù)庫的第二個重要組成部分。索引提供了一種將查詢映射到向量存儲中最相關的文檔或項目的方法,而無需計算每個查詢和每個文檔之間的相似性。
近年來,已經(jīng)發(fā)布了許多矢量商店。尤其是在LLMs領域,矢量存儲的關注度呈爆炸式增長:
現(xiàn)在讓我們選擇一個并在用例中嘗試一下。與我們在前面幾節(jié)中所做的類似,我們再次計算嵌入并將它們存儲在向量存儲中。為此,我們使用Langchain和chroma中的合適模塊作為向量存儲。
收集我們想要用來回答用戶問題的數(shù)據(jù):
importrequests
frombs4importBeautifulSoup
fromLangchain.embeddings.openaiimportOpenAIEmbeddings
fromLangchain.text_splitterimportCharacterTextSplitter
fromLangchain.vectorstoresimportChroma
fromLangchain.document_loadersimportTextLoader
#要抓取的維基百科頁面的URL
url='https://en.wikipedia.org/wiki/Prime_Minister_of_the_United_Kingdom'
#向URL發(fā)送GET請求
response=requests.get(url)
#使用BeautifulSoup解析HTML內容
soup=BeautifulSoup(response.content,'html.parser')
#查找頁面上的所有文本
text=soup.get_text()
text=text.replace('\n','')
#打開一個名為'的新文件寫入模式下的output.txt'并將文件對象存儲在變量中
withopen('output.txt','w',encoding='utf-8')asfile:
#將字符串寫入文件
file.write(文本)
2.加載數(shù)據(jù)并定義如何將數(shù)據(jù)拆分為文本塊
fromLangchain.text_splitterimportRecursiveCharacterTextSplitter
#
使用open('./output.txt',encoding='utf-8')asf:
text=f.read()
加載文檔text_splitter
=RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100,
length_function=len,
)
文本=text_splitter.create_documents([文本])
3.定義要用于計算文本塊嵌入的嵌入模型并將其存儲在向量存儲中
fromLangchain.embeddings.openaiimportOpenAIEmbeddings
fromLangchain.vectorstoresimportChroma
#定義嵌入模型
embeddings=OpenAIEmbeddings()
#使用文本塊和嵌入模型來填充我們的向量存儲
db=Chroma.from_documents(texts,embeddings)
4.計算用戶問題的嵌入,在向量存儲中找到相似的文本塊并使用它們來構建我們的提示
fromLangchain.llmsimportOpenAI
fromLangchainimportPromptTemplate
users_question="誰是英國現(xiàn)任首相?"
#使用我們的向量存儲來查找相似的文本塊
results=db.similarity_search(
query=user_question,
n_results=5
)
#定義提示模板
template="""
你是一個喜歡幫助別人的聊天機器人!給出以下上下文部分,僅使用給定的上下文回答
問題。如果您不確定并且答案沒有
明確寫在文檔中,請說“抱歉,我不知道如何提供幫助?!?br>上下文部分:
{context}
問題:
{users_question}
答案:
"""
PromptTemplate(template=template,input_variables=["context","users_question"])
#填寫提示模板Prompt_text
=Prompt.format(context=results),users_question=users_question)#詢問定義的LLMllm(prompt_text)
小結
為了使我們的LLMs能夠分析和回答有關我們數(shù)據(jù)的問題,我們通常不會對模型進行微調。相反,在微調過程中,目標是提高模型有效響應特定任務的能力,而不是教給它新信息。
就Alpaca7B而言,LLM(LLaMA)經(jīng)過微調,可以像聊天機器人一樣進行行為和交互。重點是完善模型的響應,而不是教授它全新的信息。
因此,為了能夠回答有關我們自己的數(shù)據(jù)的問題,我們使用上下文注入方法。使用上下文注入創(chuàng)建LLM應用程序是一個相對簡單的過程。主要挑戰(zhàn)在于組織和格式化要存儲在矢量數(shù)據(jù)庫中的數(shù)據(jù)。此步驟對于有效檢索上下文相似信息并確保結果可靠至關重要。