• 文章
  • 使用 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 工具实现问答搜索系统

我们既不想维护自己的神经编码器服务,甚至也不想搭建 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")
pubid问题上下文long_answer最终决定
18802997粪钙卫蛋白能否预测炎症性肠病复发风险?...测量粪钙卫蛋白可能有助于识别溃疡性结肠炎...可能
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 Issue。