使用 Sentence Transformers 和 Qdrant 构建神经搜索服务

时长:30分钟级别:初学者输出:GitHubOpen In Colab

本教程将向您展示如何构建和部署您自己的神经搜索服务,以浏览来自 startups-list.com 的公司描述并选择与您的查询最相似的公司。该网站包含每个条目的公司名称、描述、位置和图片。

神经搜索服务使用人工神经网络来提高搜索结果的准确性和相关性。除了提供简单的关键词结果外,该系统还可以按含义检索结果。它可以理解和解释复杂的搜索查询,并提供更具上下文相关性的输出,有效提升用户的搜索体验。

工作流程

要创建神经搜索服务,您需要转换原始数据,然后创建一个搜索函数来处理它。首先,您将 1) 使用 BERT 机器学习模型的修改版本下载和准备样本数据集。然后,您将 2) 将数据加载到 Qdrant 中,3) 创建一个神经搜索 API,并 4) 使用 FastAPI 提供服务。

Neural Search Workflow

注意:本教程的代码可以在此处找到:| 步骤 1:数据准备过程 | 步骤 2:神经搜索完整代码。|

先决条件

要完成本教程,您需要

  • Docker - 使用 Qdrant 最简单的方法是运行预构建的 Docker 镜像。
  • 来自 startups-list.com 的原始解析数据
  • Python 版本 >=3.8

准备样本数据集

要对初创公司描述进行神经搜索,您必须首先将描述数据编码为向量。为了处理文本,您可以使用像 BERT 或 sentence transformers 这样的预训练模型。sentence-transformers 库让您可以方便地下载和使用许多预训练模型,例如 DistilBERT、MPNet 等。

  1. 首先您需要下载数据集。
wget https://storage.googleapis.com/generall-shared-data/startups_demo.json
  1. 安装 SentenceTransformer 库以及其他相关包。
pip install sentence-transformers numpy pandas tqdm
  1. 导入所需的模块。
from sentence_transformers import SentenceTransformer
import numpy as np
import json
import pandas as pd
from tqdm.notebook import tqdm

您将使用一个名为 all-MiniLM-L6-v2 的预训练模型。这是一个性能优化的句子嵌入模型,您可以在此处阅读有关它以及其他可用模型的更多信息。

  1. 下载并创建一个预训练的句子编码器。
model = SentenceTransformer(
    "all-MiniLM-L6-v2", device="cuda"
)  # or device="cpu" if you don't have a GPU
  1. 读取原始数据文件。
df = pd.read_json("./startups_demo.json", lines=True)
  1. 编码所有初创公司描述,为每个描述创建一个嵌入向量。在内部,encode 函数会将输入分割成批次,这将显着加快处理速度。
vectors = model.encode(
    [row.alt + ". " + row.description for row in df.itertuples()],
    show_progress_bar=True,
)

所有描述现在都已转换为向量。共有 40474 个维度为 384 的向量。模型的输出层具有这个维度。

vectors.shape
# > (40474, 384)
  1. 将保存的向量下载到名为 startup_vectors.npy 的新文件中。
np.save("startup_vectors.npy", vectors, allow_pickle=False)

在 Docker 中运行 Qdrant

接下来,您需要使用向量引擎管理所有数据。Qdrant 允许您存储、更新或删除创建的向量。最重要的是,它允许您通过便捷的 API 搜索最近的向量。

注意:开始之前,请创建一个项目目录并在其中创建虚拟 Python 环境。

  1. 从 DockerHub 下载 Qdrant 镜像。
docker pull qdrant/qdrant
  1. 在 Docker 中启动 Qdrant。
docker run -p 6333:6333 \
    -v $(pwd)/qdrant_storage:/qdrant/storage \
    qdrant/qdrant

您应该看到如下输出

...
[2021-02-05T00:08:51Z INFO  actix_server::builder] Starting 12 workers
[2021-02-05T00:08:51Z INFO  actix_server::builder] Starting "actix-web-service-0.0.0.0:6333" service on 0.0.0.0:6333

通过访问 http://localhost:6333/ 测试服务。您应该在浏览器中看到 Qdrant 版本信息。

所有上传到 Qdrant 的数据都保存在 ./qdrant_storage 目录中,即使您重新创建容器也会被持久化。

上传数据到 Qdrant

  1. 安装官方 Python 客户端以便更好地与 Qdrant 交互。
pip install qdrant-client

此时,您应该在 startups_demo.json 文件中有初创公司记录,在 startup_vectors.npy 中有编码向量,并且 Qdrant 在本地机器上运行。

现在您需要编写一个脚本,将所有初创公司数据和向量上传到搜索引擎中。

  1. 为 Qdrant 创建一个客户端对象。
# Import client library
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance

client = QdrantClient("http://localhost:6333")
  1. 相关向量需要添加到集合中。为您的初创公司向量创建一个新集合。
if not client.collection_exists("startups"):
    client.create_collection(
        collection_name="startups",
        vectors_config=VectorParams(size=384, distance=Distance.COSINE),
    )
  1. 创建初创公司数据和向量的迭代器。

Qdrant 客户端库定义了一个特殊函数,允许您将数据集加载到服务中。然而,由于数据可能过多而无法适应单台计算机内存,该函数将数据的迭代器作为输入。

fd = open("./startups_demo.json")

# payload is now an iterator over startup data
payload = map(json.loads, fd)

# Load all vectors into memory, numpy array works as iterable for itself.
# Other option would be to use Mmap, if you don't want to load all data into RAM
vectors = np.load("./startup_vectors.npy")
  1. 上传数据
client.upload_collection(
    collection_name="startups",
    vectors=vectors,
    payload=payload,
    ids=None,  # Vector ids will be assigned automatically
    batch_size=256,  # How many vectors will be uploaded in a single request?
)

向量现在已上传到 Qdrant。

构建搜索 API

现在所有准备工作都已完成,让我们开始构建一个神经搜索类。

为了处理传入请求,神经搜索需要两样东西:1) 将查询转换为向量的模型,以及 2) 执行搜索查询的 Qdrant 客户端。

  1. 创建一个名为 neural_searcher.py 的文件,并指定以下内容。
from qdrant_client import QdrantClient
from sentence_transformers import SentenceTransformer


class NeuralSearcher:
    def __init__(self, collection_name):
        self.collection_name = collection_name
        # Initialize encoder model
        self.model = SentenceTransformer("all-MiniLM-L6-v2", device="cpu")
        # initialize Qdrant client
        self.qdrant_client = QdrantClient("http://localhost:6333")
  1. 编写搜索函数。
def search(self, text: str):
    # Convert text query into vector
    vector = self.model.encode(text).tolist()

    # Use `vector` for search for closest vectors in the collection
    search_result = self.qdrant_client.query_points(
        collection_name=self.collection_name,
        query=vector,
        query_filter=None,  # If you don't want any filters for now
        limit=5,  # 5 the most closest results is enough
    ).points
    # `search_result` contains found vector ids with similarity scores along with the stored payload
    # In this function you are interested in payload only
    payloads = [hit.payload for hit in search_result]
    return payloads
  1. 添加搜索过滤器。

使用 Qdrant,还可以为搜索添加一些条件。例如,如果您想搜索某个城市的初创公司,搜索查询可能如下所示

from qdrant_client.models import Filter

    ...

    city_of_interest = "Berlin"

    # Define a filter for cities
    city_filter = Filter(**{
        "must": [{
            "key": "city", # Store city information in a field of the same name 
            "match": { # This condition checks if payload field has the requested value
                "value": city_of_interest
            }
        }]
    })

    search_result = self.qdrant_client.query_points(
        collection_name=self.collection_name,
        query=vector,
        query_filter=city_filter,
        limit=5
    ).points
    ...

您现在已经创建了一个用于神经搜索查询的类。现在将其封装成一个服务。

使用 FastAPI 部署搜索

要构建服务,您将使用 FastAPI 框架。

  1. 安装 FastAPI。

要安装它,请使用命令

pip install fastapi uvicorn
  1. 实现服务。

创建一个名为 service.py 的文件,并指定以下内容。

该服务将只有一个 API 端点,如下所示

from fastapi import FastAPI

# The file where NeuralSearcher is stored
from neural_searcher import NeuralSearcher

app = FastAPI()

# Create a neural searcher instance
neural_searcher = NeuralSearcher(collection_name="startups")


@app.get("/api/search")
def search_startup(q: str):
    return {"result": neural_searcher.search(text=q)}


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)
  1. 运行服务。
python service.py
  1. 在浏览器中打开 http://localhost:8000/docs

您应该能够看到您的服务的调试界面。

FastAPI Swagger interface

您可以随意试用,对我们语料库中的公司进行查询,并查看结果。

下一步

本教程中的代码已用于开发一个在线实时演示。您可以试用它,直观了解神经搜索在哪些情况下很有用。该演示包含一个开关,用于选择神经搜索和全文搜索。您可以打开和关闭神经搜索,将其结果与常规全文搜索进行比较。

注意:本教程的代码可以在此处找到:| 步骤 1:数据准备过程 | 步骤 2:神经搜索完整代码。|

加入我们的 Discord 社区,我们在其中讨论向量搜索和相似度学习,发布神经网络和神经搜索应用的其他示例。

此页面是否有用?

感谢您的反馈!🙏

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