神经网络搜索 101:一份综合指南和分步教程
信息检索技术是使现代互联网得以存在的主要技术之一。如今,搜索技术是各种应用程序的核心。从网页搜索到产品推荐。多年来,这项技术没有太大变化,直到神经网络的出现。
在本指南中,我们将找到这些问题的答案
- 常规搜索和神经网络搜索有什么区别?
- 哪些神经网络可用于搜索?
- 在哪些任务中,神经网络搜索是有用的?
- 如何逐步构建和部署自己的神经网络搜索服务?
什么是神经网络搜索?
常规的全文搜索,例如 Google 的搜索,包括在文档中搜索关键词。因此,该算法无法考虑查询和文档的真实含义。许多用户可能感兴趣的文档由于使用了不同的措辞而未被找到。
神经网络搜索试图解决的正是这个问题——它试图通过含义而不是关键词进行搜索。为了实现这一点,搜索分两步进行。第一步,经过特殊训练的神经网络编码器将查询和搜索对象转换为称为嵌入的向量表示。编码器必须经过训练,以便相似的对象,例如具有相同含义的文本或相似的图片获得接近的向量表示。

有了这个向量表示,就很容易理解第二步应该是什么。要找到与查询相似的文档,您现在只需要找到最近的向量。确定两个向量之间距离的最便捷方法是计算余弦距离。也可以使用通常的欧几里得距离,但由于维度诅咒,它的效率不高。
可以使用哪种模型?
理想情况下,使用专门训练的模型来确定含义的接近程度。例如,在语义文本相似度 (STS) 数据集上训练的模型。当前最先进的模型可以在此排行榜上找到。
然而,不仅可以使用专门训练的模型。如果模型在足够大的数据集上训练,其内部特征也可以作为嵌入。因此,例如,您可以获取任何在 ImageNet 上预训练的模型并从中剪切掉最后一层。在神经网络的倒数第二层中,通常会形成最高级的特征,但这些特征不对应于特定的类别。这一层的输出可以用作嵌入。
神经网络搜索适用于哪些任务?
神经网络搜索在查询无法精确表述的领域具有最大的优势。在 SQL 数据库中查询表不是神经网络搜索的最佳应用场景。
相反,如果查询本身是模糊的,或者无法将其表述为一组条件,那么神经网络搜索就可以提供帮助。如果搜索查询是图片、声音文件或长文本,那么神经网络搜索几乎是唯一的选择。
如果您想构建一个推荐系统,神经网络方法也可能很有用。用户的行为可以像图片或文本一样编码到向量空间中。有了这些向量,就可以找到语义相似的用户并确定用户下一步可能的行动。
使用 Qdrant 进行分步神经网络搜索教程
综上所述,让我们进行神经网络搜索。作为一个例子,我决定根据创业公司的描述进行搜索。在此演示中,我们将看到文本搜索效果更好和神经网络搜索效果更好的情况。
我将使用来自 startups-list.com 的数据。每条记录包含公司名称、描述公司的段落、位置和图片。原始解析数据可在此链接中找到。
步骤 1:准备用于神经网络搜索的数据
为了能够在向量空间中搜索我们的描述,我们必须首先获取向量。我们需要将描述编码为向量表示。由于描述是文本数据,我们可以使用预训练的语言模型。如上所述,对于文本搜索任务,有一整套专门针对语义相似度进行调整的预训练模型。
我认为,使用预训练语言模型最简单的库之一是 UKPLab 的 sentence-transformers。它提供了一种方便地下载和使用许多预训练模型的方式,这些模型大多基于 Transformer 架构。Transformer 并不是唯一适用于神经网络搜索的架构,但对于我们的任务来说,它已经足够了。
我们将使用名为 all-MiniLM-L6-v2 的模型。该模型是一个通用模型,针对许多用例进行了调整。它在超过 10 亿个训练对的大型多样数据集上进行了训练。它针对低内存消耗和快速推理进行了优化。
包含详细注释的数据准备完整代码可以在Colab Notebook中找到并运行。
步骤 2:整合向量搜索引擎
现在我们已经有了所有记录的向量表示,我们需要将它们存储在某个地方。除了存储,我们可能还需要添加或删除向量,以及随向量保存附加信息。最重要的是,我们需要一种搜索最近向量的方法。
向量搜索引擎可以处理所有这些任务。它提供了一个方便的 API 用于搜索和管理向量。在本教程中,我们将使用 Qdrant 向量搜索引擎。它不仅支持所有必要的向量操作,还允许您与向量一起存储额外的有效载荷,并使用它来执行搜索结果的过滤。Qdrant 有一个 Python 客户端,如果您需要从其他语言使用它,它还定义了 API 模式。
使用 Qdrant 最简单的方法是运行预构建的镜像。因此,请确保您的系统上安装了 Docker。
要启动 Qdrant,请按照其主页上的说明操作。
从DockerHub下载镜像
docker pull qdrant/qdrant
并在 docker 内部运行服务
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
这意味着服务已成功启动并正在监听端口 6333。为了确保,您可以在浏览器中测试https://:6333/并获取 qdrant 版本信息。
所有上传到 Qdrant 的数据都保存在 ./qdrant_storage 目录中,即使您重新创建容器,这些数据也将持续存在。
步骤 3:将数据上传到 Qdrant
现在,一旦我们准备好向量并且搜索引擎正在运行,我们就可以开始上传数据了。要从 python 与 Qdrant 交互,我建议使用现成的客户端库。
要安装它,请使用以下命令
pip install qdrant-client
此时,我们应该在 startups.json 文件中拥有创业公司记录,在 startup_vectors.npy 文件中拥有编码向量,并且 Qdrant 在本地机器上运行。让我们编写一个脚本将所有创业公司数据和向量上传到搜索引擎中。
首先,让我们为 Qdrant 创建一个客户端对象。
# Import client library
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance
qdrant_client = QdrantClient(host='localhost', port=6333)
Qdrant 允许您将具有相同用途的向量组合成集合。许多独立的向量集合可以同时存在于一个服务上。
让我们为我们的创业公司向量创建一个新集合。
if not qdrant_client.collection_exists('startups'):
qdrant_client.create_collection(
collection_name='startups',
vectors_config=VectorParams(size=384, distance=Distance.COSINE),
)
vector_size 参数非常重要。它告诉服务该集合中向量的大小。一个集合中的所有向量必须具有相同的大小,否则无法计算它们之间的距离。384 是我们正在使用的编码器的输出维度。
distance 参数允许指定用于测量两点之间距离的函数。
Qdrant 客户端库定义了一个特殊函数,允许您将数据集加载到服务中。但是,由于数据可能太多,无法适应单个计算机内存,因此该函数将数据的迭代器作为输入。
让我们创建一个创业公司数据和向量的迭代器。
import numpy as np
import json
fd = open('./startups.json')
# payload is now an iterator over startup data
payload = map(json.loads, fd)
# Here we load all vectors into memory, numpy array works as iterable for itself.
# Other option would be to use Mmap, if we don't want to load all data into RAM
vectors = np.load('./startup_vectors.npy')
最后一步——数据上传
qdrant_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?
)
现在我们已经将向量上传到向量搜索引擎。在下一步中,我们将学习如何实际搜索最近的向量。
此步骤的完整代码可在此处找到。
步骤 4:构建搜索 API
现在所有准备工作都已完成,让我们开始构建神经网络搜索类。
首先,安装所有依赖项
pip install sentence-transformers numpy
为了处理传入请求,神经网络搜索需要两样东西。一个将查询转换为向量的模型和一个 Qdrant 客户端,用于执行搜索查询。
# File: 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(host='localhost', port=6333)
搜索函数看起来尽可能简单
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.search(
collection_name=self.collection_name,
query_vector=vector,
query_filter=None, # We don't want any filters for now
top=5 # 5 the most closest results is enough
)
# `search_result` contains found vector ids with similarity scores along with the stored payload
# In this function we are interested in payload only
payloads = [hit.payload for hit in search_result]
return payloads
使用 Qdrant,还可以为搜索添加一些条件。例如,如果我们想搜索某个城市的初创公司,搜索查询可能看起来像这样
from qdrant_client.models import Filter
...
city_of_interest = "Berlin"
# Define a filter for cities
city_filter = Filter(**{
"must": [{
"key": "city", # We store city information in a field of the same name
"match": { # This condition checks if payload field have requested value
"keyword": city_of_interest
}
}]
})
search_result = self.qdrant_client.search(
collection_name=self.collection_name,
query_vector=vector,
query_filter=city_filter,
top=5
)
...
我们现在有一个用于进行神经网络搜索查询的类。让我们将其封装成一个服务。
步骤 5:部署为服务
为了构建该服务,我们将使用 FastAPI 框架。它非常易于使用,并且只需编写最少的代码。
要安装它,请使用命令
pip install fastapi uvicorn
我们的服务将只有一个 API 端点,它将是这样的
# File: service.py
from fastapi import FastAPI
# That is the file where NeuralSearcher is stored
from neural_searcher import NeuralSearcher
app = FastAPI()
# Create an instance of the neural searcher
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)
现在,如果您使用以下命令运行服务
python service.py
并在浏览器中打开https://:8000/docs,您应该能够看到服务的调试界面。

随意使用它,进行查询并查看结果。本教程到此结束。
通过 Qdrant 的免费演示体验神经网络搜索
渴望亲身体验神经网络搜索?迈出下一步,与 Qdrant 预约免费演示!亲身体验这项尖端技术如何改变您的搜索能力。
我们的演示将帮助您培养对神经网络搜索何时有用的直觉。演示中包含一个开关,用于在神经网络搜索和全文搜索之间进行选择。您可以打开和关闭神经网络搜索,将其结果与常规全文搜索进行比较。尝试使用创业公司描述来查找相似的创业公司。
加入我们的 Discord 社区,我们在那里讨论向量搜索和相似度学习,并发布神经网络和神经网络搜索应用程序的其他示例。
