在本文中,我介绍了两个旨在解决这些问题的开源解决方案:
这些工具应用于 NPR,这是一个新闻门户推荐数据集,旨在支持学术界开发推荐算法。
首先,我们需要找到一种方法将输入数据转换成向量,我们将这些向量称为嵌入。
那么,让我们看看能够使用NPR数据集处理哪种类型的数据:
import pandas as pd
df = pd.read_parquet("articles.parquet")
df.tail()
我们可以看到,NPR拥有一些有趣的文本数据,比如文章的标题和正文内容。我们可以在下图所示的嵌入生成过程中使用它们:
一旦我们从输入数据中定义了文本特征,我们就需要建立一个嵌入模型来生成我们的数值表示。幸运的是,像HuggingFace这样的网站上有许多预训练模型,适用于特定语言或任务。在我们的示例中,我们可以使用neuralmind/bert-base-portuguese-cased模型,它是针对巴西葡萄牙语训练的,用于以下任务:
从代码角度来看,这就是我们如何翻译嵌入生成过程:
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, ...])
所以,给定一个示例输入数据,我们可以将标题和标签内容串联成一个单独的文本,并将其传递给编码器以生成文本嵌入。
我们可以对NPR数据集中的所有其他文章应用相同的过程:
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)
储存嵌入向量
由于生成嵌入向量可能是一个耗费资源的过程,我们可以使用向量数据库来存储这些嵌入向量,并基于多种策略执行查询。
有几种向量数据库软件可以完成这项任务,但在本文中我将使用Qdrant,这是一个开源解决方案,它为流行的编程语言如Python、Go和Typescript提供了API。
设置Qdrant
为了处理所有Qdrant操作,我们需要创建一个指向向量数据库的客户端对象。Qdrant允许你创建一个免费的服务层以测试远程连接数据库,但为了简单起见,我将在本地创建并持久化数据库:
from qdrant_client import QdrantClient
client = QdrantClient(path="./qdrant_data")
连接建立后,我们就可以在数据库中创建一个集合来存储新闻文章的嵌入表示。
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(collections=[CollectionDescription(name='news-articles')])
请注意,矢量配置参数被用来创建集合。这些参数告诉Qdrant一些关于向量的属性,比如它们的大小以及当比较向量时使用的距离度量。
生成向量点
在最终填充数据库之前,我们需要创建适当的对象以便上传。在Qdrant中,向量可以使用PointStruct类存储,你可以用它来定义以下属性:
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()
上传向量
最后,在所有项目被转换为点结构后,我们可以将它们分块上传到数据库:
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()
)
查询向量
现在集合终于被向量填满了,我们可以开始对数据库进行查询了。我们可以通过多种方式输入信息来查询数据库,但我认为有两个非常有用的输入方式:
使用输入向量查询向量
假设我们构建这个向量数据库是为了在搜索引擎中使用。在这种情况下,我们期望用户的输入是一段输入文本,我们必须返回最相关的项目。
由于向量数据库中的所有操作都是用……向量来进行的,我们首先需要将用户的输入文本转换成一个向量,这样我们才能找到基于那个输入的相似项目。回想一下,我们使用句子转换器(Sentence Transformers)来将文本数据编码成嵌入向量,所以我们可以使用完全相同的编码器来为用户的输入文本生成一个数值表示。
既然NPR包含新闻文章,假设用户输入了“Donald Trump”来了解美国选举:
query_text = "Donald Trump"
query_vector = encoder.encode(query_text).tolist()
print (query_vector)
# output: [-0.048, -0.120, 0.695, ...]
一旦计算出输入查询向量,我们就可以在集合中搜索最接近的向量,并定义我们希望从这些向量中获取哪种类型的输出,比如它们的新闻ID、标题和主题:
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
)
在运行此操作后,这里是生成的输出标题:
似乎除了带来与特朗普本人相关的新闻外,嵌入模型也成功代表了与总统选举相关的话题。注意,在第一个输出中,并没有直接提及输入术语“Donald Trump”,除了总统选举的相关性。
使用输入向量 ID 查询向量
最后,我们可以请求向量数据库“推荐”一些离我们想要的向量 ID 近但远离不需要的向量 ID 的项。所需和不需要的 ID 分别被称为正面和负面示例,它们被视为推荐的种子。
例如,假设我们有如下正面 ID:
seed_id = '8bc22460-532c-449b-ad71-28dd86790ca2'
# title (translated): 'Learn why Joe Biden launched his bid for re-election this Tuesday'
我们可以然后请求与这个例子相似的物品:
client.recommend(
collection_name="news-articles",
positive=[seed_id],
negative=None,
with_payload=["newsId", "title", "topics"]
)
在运行此操作后,以下是翻译后的标题:
结论
本文展示了如何结合LLM和向量数据库来提供推荐。特别是,使用Sentence Transformers从NPR数据集中的文本新闻文章生成数值表示(嵌入)。一旦计算出这些嵌入,就可以填充向量数据库例如Qdrant,它便于基于多种策略查询向量。