• 文章
  • 使用 Cohere 和 Qdrant 实现问答即服务
返回实用示例

使用 Cohere 和 Qdrant 实现问答即服务

Kacper Łukawski

·

2022年11月29日

Question Answering as a Service with Cohere and Qdrant

双编码器可能是设置语义问答系统最有效的方式。这种架构依赖于同一个神经网络模型,该模型为问题和答案创建向量嵌入。其假设是,在潜在空间中,问题和答案的表示应该彼此接近。之所以如此,是因为它们都应该描述相同的语义概念。但这不适用于“是”或“否”之类的答案,但标准 FAQ 式问题会更容易一些,因为通常这两种文本之间存在重叠。不一定是在措辞上,而是在语义上。

Bi-encoder structure. Both queries (questions) and documents (answers) are vectorized by the same neural encoder.
Output embeddings are then compared by a chosen distance function, typically cosine similarity.

是的,你需要自带嵌入才能开始。获取嵌入有多种方法,但使用 Cohere co.embed API 可能是最简单便捷的方法。

为什么 co.embed API 和 Qdrant 配合得很好?

维护一个大型语言模型可能既困难又昂贵。当流量变化时,对其进行扩展和缩减需要更多的努力,并且变得不可预测。这对于任何语义搜索系统来说都可能是一个障碍。但是,如果你想立即开始,可以考虑使用 SaaS 模型,特别是 Cohere 的 co.embed API。它为你提供了最先进的语言模型,作为高度可用的 HTTP 服务,无需训练或维护自己的服务。由于所有通信都通过 JSON 完成,你可以简单地将 co.embed 输出作为 Qdrant 输入。

# Putting the co.embed API response directly as Qdrant method input
qdrant_client.upsert(
    collection_name="collection",
    points=rest.Batch(
        ids=[...],
        vectors=cohere_client.embed(...).embeddings,
        payloads=[...],
    ),
)

这两个工具易于结合,因此你可以在几分钟内(而不是几天)开始使用语义搜索。

如果你的需求非常具体,需要对通用模型进行微调怎么办?Co.embed API 超越了预训练编码器,允许提供一些自定义数据集来使用你自己的数据自定义嵌入模型。因此,你可以在不担心基础设施的情况下获得领域特定模型的质量。

系统架构概述

在实际系统中,答案会被向量化并存储在高效的向量搜索数据库中。我们通常甚至不需要提供具体的答案,而只需使用文本的句子或段落并对其进行向量化。不过,如果一段稍长的文本包含特定问题的答案,它与问题嵌入的距离不应该太远。而且肯定比所有其他不匹配的答案更近。将答案嵌入存储在向量数据库中使搜索过程变得更容易。

Building the database of possible answers. All the texts are converted into their vector embeddings and those
embeddings are stored in a vector database, i.e. Qdrant.

寻找正确答案

一旦我们的数据库正常工作且所有答案嵌入都已到位,我们就可以开始查询它了。我们基本上对给定问题执行相同的向量化操作,并要求数据库提供一些近邻。我们依赖于嵌入彼此接近,因此我们期望潜在空间中距离最小的点包含正确的答案。

While searching, a question gets vectorized by the same neural encoder. Vector database is a component that looks
for the closest answer vectors using i.e. cosine similarity. A proper system, like Qdrant, will make the lookup
process more efficient, as it won’t calculate the distance to all the answer embeddings. Thanks to HNSW, it will
be able to find the nearest neighbours with sublinear complexity.

使用 SaaS 工具实现 QA 搜索系统

我们不想维护自己的神经网络编码器服务,甚至不想设置 Qdrant 实例。这两种情况都有 SaaS 解决方案——Cohere 的 co.embed APIQdrant Cloud,所以我们将使用它们而不是本地工具。

生物医学数据上的问答

我们将为生物医学数据实现问答系统。有一个 pubmed_qa 数据集,其中 pqa_labeled 子集包含 1,000 个由领域专家标记的问题和答案示例。我们的系统将使用 co.embed API 生成的嵌入,并将其加载到 Qdrant。使用 Qdrant Cloud 与你自己的实例在此处差别不大。连接到云实例的方式存在细微差异,但所有其他操作都以相同的方式执行。

from datasets import load_dataset

# Loading the dataset from HuggingFace hub. It consists of several columns: pubid, 
# question, context, long_answer and final_decision. For the purposes of our system, 
# we’ll use question and long_answer.
dataset = load_dataset("pubmed_qa", "pqa_labeled")
出版物ID问题上下文长答案最终决定
18802997钙卫蛋白能否预测炎症性肠病复发风险?...测量钙卫蛋白可能有助于识别 UC...可能
20538207肾脏移植期间是否应监测体温?...新的存储方式可以提供更稳定的温度...
25521278盘子清空是肥胖的危险因素吗?...吃饭时清空盘子的倾向...
17595200是否存在宫内环境对肥胖的影响?...母子和父子比较...
15280782艾滋病感染者中不安全性行为是否增加?...没有证据表明不安全性行为有增加的趋势...

使用 Cohere 和 Qdrant 构建答案数据库

为了开始生成嵌入,你需要创建一个 Cohere 账户。这将启动你的试用期,因此你将能够免费对文本进行向量化。登录后,你的默认 API 密钥将显示在设置中。我们将需要它来通过官方 python 包调用 co.embed API。

import cohere

cohere_client = cohere.Client(COHERE_API_KEY)

# Generating the embeddings with Cohere client library
embeddings = cohere_client.embed(
    texts=["A test sentence"],
    model="large",
)
vector_size = len(embeddings.embeddings[0])
print(vector_size) # output: 4096

让我们首先连接到 Qdrant 实例并创建一个具有适当配置的集合,以便我们以后可以将一些嵌入放入其中。

# Connecting to Qdrant Cloud with qdrant-client requires providing the api_key.
# If you use an on-premise instance, it has to be skipped.
qdrant_client = QdrantClient(
    host="xyz-example.eu-central.aws.cloud.qdrant.io", 
    prefer_grpc=True,
    api_key=QDRANT_API_KEY,
)

现在我们能够对所有答案进行向量化。它们将构成我们的集合,因此我们也可以将它们与有效负载和标识符一起放入 Qdrant。这将使我们的数据集易于搜索。

answer_response = cohere_client.embed(
    texts=dataset["train"]["long_answer"],
    model="large",
)
vectors = [
    # Conversion to float is required for Qdrant
    list(map(float, vector)) 
    for vector in answer_response.embeddings
]
ids = [entry["pubid"] for entry in dataset["train"]]

# Filling up Qdrant collection with the embeddings generated by Cohere co.embed API
qdrant_client.upsert(
    collection_name="pubmed_qa", 
    points=rest.Batch(
        ids=ids,
        vectors=vectors,
        payloads=list(dataset["train"]),
    )
)

就是这样。我们甚至没有设置自己的单个服务器,就创建了一个可以轻松提问的系统。我不想称之为无服务器,因为这个术语已经被使用了,但 co.embed API 与 Qdrant Cloud 使一切维护起来都更容易。

通过语义搜索回答问题 — 质量

是时候用一些问题来查询我们的数据库了。衡量系统整体质量可能很有趣。在这种类型的问题中,我们通常使用top-k 准确率。我们假设如果正确答案出现在前k个结果中,则系统的预测是正确的。

# Finding the position at which Qdrant provided the expected answer for each question. 
# That allows to calculate accuracy@k for different values of k.
k_max = 10
answer_positions = []
for embedding, pubid in tqdm(zip(question_response.embeddings, ids)):
    response = qdrant_client.search(
        collection_name="pubmed_qa",
        query_vector=embedding,
        limit=k_max,
    )

    answer_ids = [record.id for record in response]
    if pubid in answer_ids:
        answer_positions.append(answer_ids.index(pubid))
    else:
        answer_positions.append(-1)

保存的答案位置允许我们计算不同k值的指标。

# Prepared answer positions are being used to calculate different values of accuracy@k
for k in range(1, k_max + 1):
    correct_answers = len(
        list(
            filter(lambda x: 0 <= x < k, answer_positions)
        )
    )
    print(f"accuracy@{k} =", correct_answers / len(dataset["train"]))

以下是不同 k 值对应的 top-k 准确率值

指标
accuracy@10.877
accuracy@20.921
accuracy@30.942
accuracy@40.950
accuracy@50.956
accuracy@60.960
accuracy@70.964
accuracy@80.971
accuracy@90.976
accuracy@100.977

看起来我们的系统即使只考虑距离最小的第一个结果,也运行得相当好。我们大约有 12% 的问题未能成功。但随着 k 值的增加,准确率也随之提高。检查我们的系统未能回答的问题,以及它们的完美匹配和我们的猜测,可能也很有价值。

我们仅用几行代码就成功实现了一个可用的问答系统。如果你对所达到的结果感到满意,那么你可以立即开始使用它。不过,如果你觉得需要稍作改进,那么微调模型是一个不错的选择。如果你想查看完整的源代码,可以在 Google Colab 上找到。

此页面有用吗?

感谢您的反馈!🙏

很抱歉听到这个消息。😔 你可以在 GitHub 上编辑此页面,或者创建一个 GitHub 问题。