譯者 | 朱先忠
審校 | 重樓
近年來(lái),隨著諸如ChatGPT、Bard等生成式人工智能工具的發(fā)布,大型語(yǔ)言模型(LLM)在機(jī)器學(xué)習(xí)社區(qū)引起了全球熱議。這些解決方案背后的核心思想之一是計(jì)算非結(jié)構(gòu)化數(shù)據(jù)(如文本和圖像)的數(shù)字表示,并找出這些表示之間的相似之處。
然而,將所有這些概念應(yīng)用到生產(chǎn)環(huán)境中存在其自身的一系列機(jī)器學(xué)習(xí)工程挑戰(zhàn):
- 如何快速生成這些表示?
- 如何將它們存儲(chǔ)在適當(dāng)?shù)臄?shù)據(jù)庫(kù)中?
- 如何快速計(jì)算生產(chǎn)環(huán)境的相似性?在這篇文章中,我將介紹兩種開源解決方案,目的是解決下面這些問題:
- 句子變換器(https://www.sbert.net/;參考引文1):一種基于文本信息的嵌入生成技術(shù);
- Qdrant(https://qdrant.tech/):一種能夠存儲(chǔ)嵌入并提供簡(jiǎn)單的查詢接口的向量數(shù)據(jù)庫(kù)。這兩個(gè)工具都將應(yīng)用于開發(fā)本文中的新聞門戶推薦系統(tǒng)(參考引文2)。NPR(News Portal Recommendation),即新聞門戶推薦數(shù)據(jù)集(在Kaggle網(wǎng)絡(luò)公開免費(fèi)使用:https://www.kaggle.com/datasets/joelpl/news-portal-recommendations-npr-by-globo),旨在支持學(xué)術(shù)界開發(fā)推薦算法。在本文的最后,您將學(xué)會(huì):
- 使用句子轉(zhuǎn)換器生成新聞嵌入
- 使用Qdrant數(shù)據(jù)庫(kù)存儲(chǔ)嵌入
- 查詢嵌入以推薦新聞文章需要說明的是,本文的所有代碼您都可以在Github網(wǎng)站上獲得。
1.使用句子轉(zhuǎn)換器生成嵌入
首先,我們需要找到一種將輸入數(shù)據(jù)轉(zhuǎn)換為向量的方法,我們稱之為嵌入(如果你想深入了解嵌入概念,我推薦您閱讀一下Boykis的文章《什么是嵌入?》,參考引文3:https://vickiboykis.com/what_are_embeddings/about.html)。
因此,首先讓我們來(lái)看看我們可以使用NPR數(shù)據(jù)集處理什么樣的數(shù)據(jù):
import pandas as pd
df = pd.read_parquet("articles.parquet")
df.tail()
NPR數(shù)據(jù)集提供的樣本數(shù)據(jù)(圖片由作者本人生成)
NPR數(shù)據(jù)集提供了一些有趣的文本數(shù)據(jù),如文章的標(biāo)題和正文內(nèi)容等。我們可以在嵌入生成過程中使用它們,如下圖所示:
嵌入生成過程(作者本人提供的圖片)
這樣一來(lái),一旦我們從輸入數(shù)據(jù)中定義了文本特征,我們就需要建立一個(gè)嵌入模型來(lái)生成我們的數(shù)字表示。幸運(yùn)的是,存在像HuggingFace這樣的網(wǎng)站,你可以在那里尋找適合特定語(yǔ)言或任務(wù)的預(yù)訓(xùn)練模型。在我們的例子中,我們可以使用neuralmind/bert-base-portuguese-cased模型,該模型是用巴西葡萄牙語(yǔ)訓(xùn)練的,用于以下任務(wù):
- 命名實(shí)體識(shí)別
- 句子文本相似性
- 文本蘊(yùn)含識(shí)別下面的實(shí)現(xiàn)代碼展示了我們是如何翻譯嵌入生成過程的:
from sentence_transformers import SentenceTransformer
model_name = "neuralmind/bert-base-portuguese-cased"
encoder = SentenceTransformer(model_name_or_path=model_name)
title = """
Paraguaios v?o às urnas neste domingo (30) para escolher novo presidente
"""
sentence = title
sentence_embedding = encoder.encode(sentence)
print (sentence_embedding)
# output: np.array([-0.2875876, 0.0356041, 0.31462672, 0.06252239, ...])
根據(jù)這里的代碼邏輯,給定一個(gè)樣本輸入數(shù)據(jù),我們就可以將標(biāo)題和標(biāo)簽內(nèi)容連接到單個(gè)文本中,并將其傳遞給編碼器以生成文本嵌入。
我們可以對(duì)NPR數(shù)據(jù)集中的所有其他文章應(yīng)用于上面相同的過程:
def generate_item_sentence(item: pd.Series, text_columns=["title"]) -> str:
return ' '.join([item[column] for column in text_columns])
df["sentence"] = df.apply(generate_item_sentence, axis=1)
df["sentence_embedding"] = df["sentence"].apply(encoder.encode)
請(qǐng)注意:上面這個(gè)過程可能需要耗費(fèi)更長(zhǎng)的時(shí)間,具體情況取決于您的機(jī)器的處理能力。
一旦我們有了所有新聞文章的嵌入;接下來(lái),我們就可以定義一個(gè)存儲(chǔ)它們的策略了。
2.存儲(chǔ)嵌入
由于生成嵌入可能是一個(gè)昂貴的過程;因此,我們可以使用向量數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)這些嵌入并基于不同的策略執(zhí)行有關(guān)查詢。
目前,已經(jīng)存在幾個(gè)向量數(shù)據(jù)庫(kù)軟件可以實(shí)現(xiàn)這項(xiàng)任務(wù),但我將在本文中選擇使用Qdrant,這是一個(gè)開源解決方案,它提供了可用于Python、Go和Typescript等多種流行編程語(yǔ)言的API支持。為了更好地比較這些向量數(shù)據(jù)庫(kù),請(qǐng)查看引文4來(lái)了解更多有關(guān)詳情。
Qdrant設(shè)置準(zhǔn)備
為了處理所有的Qdrant操作,我們需要?jiǎng)?chuàng)建一個(gè)指向向量數(shù)據(jù)庫(kù)的客戶端對(duì)象。Qdrant允許您創(chuàng)建一個(gè)免費(fèi)的層服務(wù)來(lái)測(cè)試與數(shù)據(jù)庫(kù)的遠(yuǎn)程連接,但為了簡(jiǎn)單起見,我選擇在本地創(chuàng)建并保持?jǐn)?shù)據(jù)庫(kù):
from qdrant_client import QdrantClient
client = QdrantClient(path="./qdrant_data")
一旦建立了這種連接,我們就可以在數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè)集合,用于存儲(chǔ)新聞文章嵌入:
from qdrant_client import models
from qdrant_client.http.models import Distance, VectorParams
client.create_collection(
collection_name = "news-articles",
vectors_config = models.VectorParams(
size = encoder.get_sentence_embedding_dimension(),
distance = models.Distance.COSINE,
),
)
print (client.get_collections())
# output: CollectionsResponse(collectinotallow=[CollectionDescription(name='news-articles')])
請(qǐng)注意,代碼中的向量配置參數(shù)是用于創(chuàng)建集合的。這些參數(shù)告訴Qdrant向量的一些屬性,比如它們的大小和比較向量時(shí)要使用的距離指標(biāo)(我會(huì)使用余弦相似性,不過你也可以使用例如內(nèi)積或歐幾里得距離等其他的計(jì)算策略)。
生成向量點(diǎn)
在最終存儲(chǔ)到數(shù)據(jù)庫(kù)之前,我們需要?jiǎng)?chuàng)建合適的上傳對(duì)象。在Qdrant數(shù)據(jù)庫(kù)中,向量可以使用PointStruct類存儲(chǔ),您可以使用該類定義以下屬性:
- id:向量的id(在NPR的情況下,是newsId)
- vector:表示向量的一維數(shù)組(由嵌入模型生成)
- payload:一個(gè)包含任何其他相關(guān)元數(shù)據(jù)的字典,這些元數(shù)據(jù)稍后可以用于查詢集合中的向量(在NPR的情況下,是文章的標(biāo)題、正文和標(biāo)簽)。
from qdrant_client.http.models import PointStruct
metadata_columns = df.drop(["newsId", "sentence", "sentence_embedding"], axis=1).columns
def create_vector_point(item:pd.Series) -> PointStruct:
"""Turn vectors into PointStruct"""
return PointStruct(
id = item["newsId"],
vector = item["sentence_embedding"].tolist(),
payload = {
field: item[field]
for field in metadata_columns
if (str(item[field]) not in ['None', 'nan'])
}
)
points = df.apply(create_vector_point, axis=1).tolist()
上傳向量
最后,在將所有信息都轉(zhuǎn)換成點(diǎn)結(jié)構(gòu)后,我們就可以將它們分塊上傳到數(shù)據(jù)庫(kù)中:
CHUNK_SIZE = 500
n_chunks = np.ceil(len(points)/CHUNK_SIZE)
for i, points_chunk in enumerate(np.array_split(points, n_chunks)):
client.upsert(
collection_name="news-articles",
wait=True,
points=points_chunk.tolist()
)
3.查詢向量
現(xiàn)在,既然我們已經(jīng)用向量存儲(chǔ)滿集合,接下來(lái),我們就可以開始查詢數(shù)據(jù)庫(kù)了。我們可以通過多種方式輸入信息來(lái)查詢數(shù)據(jù)庫(kù),但我認(rèn)為這兩種非常有用的輸入可以使用:
- 輸入文本
- 輸入向量ID
3.1 使用輸入向量查詢向量
假設(shè)我們已經(jīng)成功構(gòu)建了用于搜索引擎的上述向量數(shù)據(jù)庫(kù),我們希望用戶的輸入是一個(gè)輸入文本,并且我們必須返回最相關(guān)的內(nèi)容。
由于向量數(shù)據(jù)庫(kù)中的所有操作都是使用向量來(lái)實(shí)現(xiàn)的,所以,我們首先需要將用戶的輸入文本轉(zhuǎn)換為向量,這樣我們就可以根據(jù)該輸入找到類似的內(nèi)容。回想一下,我們曾經(jīng)使用句子轉(zhuǎn)換器將文本數(shù)據(jù)編碼到嵌入中;因此,我們也可以使用相同的編碼器為用戶的輸入文本生成數(shù)字表示。
由于NPR包含了新聞文章,那么假設(shè)用戶鍵入“Donald Trump”(唐納德·特朗普)來(lái)了解美國(guó)大選信息:
query_text = "Donald Trump"
query_vector = encoder.encode(query_text).tolist()
print (query_vector)
# output: [-0.048, -0.120, 0.695, ...]
一旦計(jì)算出輸入查詢向量,我們就可以搜索集合中最接近的向量,并定義我們希望從這些向量中得到什么樣的輸出,比如它們的newsId、標(biāo)題和主題:
from qdrant_client.models import Filter
from qdrant_client.http import models
client.search(
collection_name="news-articles",
query_vector=query_vector,
with_payload=["newsId", "title", "topics"],
query_filter=None
)
注意:默認(rèn)情況下,Qdrant使用近似最近鄰居算法來(lái)快速掃描嵌入,但是您也可以進(jìn)行完全掃描,并帶來(lái)準(zhǔn)確的最近鄰數(shù)據(jù)——請(qǐng)記住,這是一個(gè)更昂貴的操作。
運(yùn)行上面的操作后,以下是生成的輸出標(biāo)題(為了更好地理解,翻譯成了英語(yǔ)):
- 輸入句子:Donald Trump(唐納德·特朗普)
- 輸出1:Paraguayans go to the polls this Sunday (30) to choose a new president(巴拉圭人將于本周日(30日)前往投票站選舉新總統(tǒng))
- 輸出2:Voters say Biden and Trump should not run in 2024, Reuters/Ipsos poll shows(路透社/益普索民意調(diào)查顯示,選民表示拜登和特朗普不應(yīng)在2024年參選)
- 輸出3:Writer accuses Trump of sexually abusing her in the 1990s(作家指責(zé)特朗普在20世紀(jì)90年代對(duì)她進(jìn)行性虐待)
- 輸出4:Mike Pence, former vice president of Donald Trump, gives testimony in court that could complicate the former president(唐納德·特朗普的前副總統(tǒng)邁克·彭斯在法庭上作證,這可能會(huì)給前總統(tǒng)帶來(lái)不少麻煩)似乎除了帶來(lái)與特朗普本人有關(guān)的新聞外,嵌入模型還成功地描述了與總統(tǒng)選舉有關(guān)的話題。請(qǐng)注意,在第一個(gè)輸出中,除了總統(tǒng)選舉之外,沒有直接引用輸入術(shù)語(yǔ)“唐納德·特朗普”。
此外,我還省略了query_filter參數(shù)。如果您想指定輸出必須滿足某些給定條件,這是一個(gè)非常有用的工具。例如,在新聞門戶網(wǎng)站中,通常只過濾最近的文章(比如從過去7天起),這是很重要的。因此,您可以查詢滿足最小發(fā)布時(shí)間戳的新聞文章。
注意:在新聞推薦場(chǎng)景下,存在諸如公平性和多樣性等多個(gè)需要考慮的方面。當(dāng)然,這是一個(gè)開放的討論主題;但是,如果您對(duì)這一領(lǐng)域感興趣的話,不妨參閱NORMalize研討會(huì)上的文章。
3.2 使用輸入向量ID查詢向量
最后,我們可以要求向量數(shù)據(jù)庫(kù)“推薦”更接近某些所需向量ID但遠(yuǎn)離不需要的向量ID的內(nèi)容。期望的ID和不期望的ID分別被稱為正樣本和負(fù)樣本,它們被認(rèn)為是推薦的種子樣本。
例如,假設(shè)我們有以下正樣本ID:
seed_id = '8bc22460-532c-449b-ad71-28dd86790ca2'
# title (translated): 'Learn why Joe Biden launched his bid for re-election this Tuesday'
那么,我們就可以要求提供與此樣本類似的內(nèi)容:
client.recommend(
collection_name="news-articles",
positive=[seed_id],
negative=None,
with_payload=["newsId", "title", "topics"]
)
運(yùn)行上面的操作后,以下是已翻譯的輸出標(biāo)題:
- 輸入項(xiàng):Learn why Joe Biden launched his bid for re-election this Tuesday(了解喬·拜登本周二發(fā)起連任競(jìng)選的原因)
- 輸出1:Biden announces he will run for re-election(拜登宣布將競(jìng)選連任)
- 產(chǎn)出2:USA: the 4 reasons that led Biden to run for re-election(美國(guó):導(dǎo)致拜登競(jìng)選連任的4個(gè)原因)
- 產(chǎn)出3:Voters say Biden and Trump should not run in 2024, Reuters/Ipsos poll shows(路透社/益普索民意調(diào)查顯示,選民表示拜登和特朗普不應(yīng)在2024年參選)
- 輸出4:Biden’s advisor’s gaffe that raised doubts about a possible second government after the election(拜登顧問的失態(tài)引發(fā)了人們對(duì)大選后可能成立第二屆政府的懷疑)
結(jié)論
本文向您展示了如何將LLM和向量數(shù)據(jù)庫(kù)結(jié)合起來(lái)構(gòu)建一個(gè)新聞推薦系統(tǒng)。特別提到了,使用句子轉(zhuǎn)換器來(lái)實(shí)現(xiàn)從NPR數(shù)據(jù)集中的文本新聞文章中生成數(shù)字表示(嵌入)的方法。一旦計(jì)算出這些嵌入,就可以用這些嵌入來(lái)填充例如Qdrant這樣的向量數(shù)據(jù)庫(kù),Qdrant的使用將非常有助于通過多種策略來(lái)實(shí)現(xiàn)向量查詢。
最后,您可以基于本文提供的基礎(chǔ)示例進(jìn)行大量進(jìn)一步的改進(jìn),例如:
- 測(cè)試其他嵌入模型
- 測(cè)試其他距離指標(biāo)
- 測(cè)試其他向量數(shù)據(jù)庫(kù)
- 使用Go等基于編譯型編程語(yǔ)言以獲得更好的性能
- 創(chuàng)建API支持的推薦系統(tǒng)
換言之,您可以提出許多想法來(lái)改進(jìn)基于LLM推薦技術(shù)的機(jī)器學(xué)習(xí)工程。所以,如果您想分享您對(duì)這些改進(jìn)的想法,請(qǐng)毫不猶豫地給我發(fā)信息吧。
關(guān)于我本人
我是巴西媒體科技公司Globo的資深數(shù)據(jù)科學(xué)家。在公司的推薦團(tuán)隊(duì)工作,我身邊有一個(gè)了不起、才華橫溢的團(tuán)隊(duì),他們付出了大量努力,通過G1、GE、Globoplay等數(shù)字產(chǎn)品向數(shù)百萬(wàn)用戶提供個(gè)性化內(nèi)容。如果沒有他們不可或缺的幫助,這篇文章是不可能與各位讀者見面的。
參考文獻(xiàn)
[1]N. reimers and I. Gurevych, Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks (2019), Association for Computational Linguistics。
[2]J. Pinho, J. Silva and L. Figueiredo, NPR: a News Portal Recommendations dataset (2023), ACM Conference on Recommender Systems。
[3]V. Boykis, What are embeddings?,個(gè)人博客。
[4]M. Ali, The Top 5 Vector Databases (2023),DataCamp博客。
譯者介紹
朱先忠,51CTO社區(qū)編輯,51CTO專家博客、講師,濰坊一所高校計(jì)算機(jī)教師,自由編程界老兵一枚。
原文標(biāo)題:Large Language Models and Vector Databases for News Recommendations,作者:Jo?o Felipe Guedes