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