阿里云向量检索服务 | 全性能搜索方案
今天主要测评阿里云向量检索服务,测评地址: https://developer.aliyun.com/topic/dashvector 。
在测评开始之前,还是需要先了解一下这款云产品,那么问题来了,什么是向量检索服务?
什么是向量检索服务
这里节录一段官方关于向量检索服务的描述:【向量检索服务基于阿里云自研的向量引擎 Proxima 内核,提供具备水平拓展、全托管、云原生的高效向量检索服务。向量检索服务将强大的向量管理、查询等能力,通过简洁易用的 SDK/API 接口透出,方便在大模型知识库搭建、多模态 AI 搜索等多种应用场景上集成。】 向量检索服务产品首页:https://www.aliyun.com/product/ai/dashvector
其实,通过官方提供向量检索服务的定义还是不太理解什么是向量检索服务。毕竟官方的定义往往比较专业,专业带来的结果就是比较晦涩难懂。那么我在这里基于一个大家在日常生活中经常会用到的场景来解释一下。
拍照搜商品
拍照搜商品用官方的话语描述就是【电商智能搜索和偏好推荐场景】,怎么理解呢?在日常生活中你走在大街上,当你看到某个东西觉得不错,比如衣服、鞋子、手办等等,那么你是否会拿出手机,点开购物APP的拍照搜商品呢?那么这个时候你用到的就是【电商智能搜索】,而当你搜索完一件商品之后,不管是通过图片还是文字搜索,购物APP都会在首页或者搜索栏下面为你推荐相似的商品,这就是【偏好推荐场景】。到这里,你是不是大概理解了什么是向量检索服务呢?
为了更好的理解什么是【电商智能搜索和偏好推荐场景】,这里我查阅了官方的描述,还是可以理解的,节录如下:【在电商智能搜索和偏好推荐场景中,向量数据库可以实现基于向量相似度的搜索和推荐功能。例如一个电商平台中包含了各种商品的图像和描述信息,用户在搜索商品时,可以通过图像或者描述信息查询相关的商品,并且还希望能够实现推荐功能,自动向用户推荐可能感兴趣的商品。
用户只需要先将商品的图像和描述信息使用Embedding技术转换为向量表示,并将其存储到向量数据库中。当用户输入查询请求时,向量检索服务可以将其转换为向量表示,然后计算查询向量与向量数据库中所有商品向量的相似度,然后返回相似度最高的几个商品向量。另外,还可以基于用户的历史行为和偏好通过向量检索服务将用户的历史浏览记录和购买记录转化为向量表示,并在向量数据库中查询与该向量最相似以及相似度较高的商品向量,为用户推荐可能感兴趣的商品,提供更加智能和个性化的服务、更加高效和优秀的性能与购买体验。】检索流程如下图
到这里,我想大家对于向量检索服务,一定都会有一个比较明确的认知了吧。
AI问答
当然,向量检索服务可不是只有拍照搜商品或者是文字搜商品这一个应用场景的哈,还有其他的应用场景。
随着大模型深度学习的发展,大模型需要利用向量来表示复杂的数据,向量数据库能够高效存储和检索这些高维向量数据,为大模型提供强大的数据支撑。另外,向量数据库可以通过关联真实世界的数据点,如实体、关系等,构建知识图谱,进而支持模型查询和验证语义信息,从而降低模型输出错误的概率。在大模型中,需要处理的数据量极大,传统的关系型数据库几乎无法满足性能需求。而向量数据库由于其内部优化和并行处理能力,可以高效地处理大规模数据集,提供快速的查询和算法执行速度。
这也就是向量检索服务的另一个应用场景【自然语言处理等AI问答系统场景】,下面节录一段这个应用场景的官方描述:【问答系统是属于自然语言处理领域的常见现实应用。典型的问答系统比如通义千问、ChatGPT、在线客户服务系统、QA聊天机器人等。例如在一个问答系统,其中包含了一些预定义的问题和对应的答案。用户希望能够根据输入的问题,自动匹配到最相似的预定义问题,并返回对应的答案。为了实现此功能,首先可以通过向量检索服务将预定义的问题和答案转换为向量表示,并将其存储到向量数据库中。其次当用户输入问题时,向量检索服务可以将其转换为向量表示,并在向量数据库中查询与该向量最相似的问题向量。然后使用模型训练、问答推理、后期优化等步骤,实现类似通义千问、ChatGPT等的语言智能交互体系。】如图所示
除了这两个现在我们接触的比较多的应用场景之外,向量检索服务还可以用于【图库类网站多模态搜索场景】、【视频检索场景】等。总之,简单的理解就是,向量检索服务在一些场景上的应用相对于传统检索服务来说,检索效能有质的飞跃。下面来体验向量检索服务吧!
基于向量检索服务与TextEmbedding实现语义搜索
简单介绍一下这个场景,基于QQ 浏览器搜索标题语料库(QBQTC:QQ Browser Query Title Corpus)进行实时的文本语义搜索,查询最相似的相关标题。
在体验开始之前需要再介绍一下向量检索服务的核心点 Embedding。
什么是Embedding
通用文本向量模型会将一段文本变成一个向量,将文本变成向量的过程叫 Embedding。
节录官方定义【Embedding是一个多维向量的表示数组,通常由一系列数字组成。Embedding可以用来表示任何数据,例如文本、音频、图片、视频等等,通过Embedding我们可以编码各种类型的非结构化数据,转化为具有语义信息的多维向量,并在这些向量上进行各种操作,例如相似度计算、聚类、分类和推荐等。】流程如图
简单的理解就像这样一个场景,输入文本:"衣服的质量杠杠的,很漂亮,不枉我等了这么久啊,喜欢,以后还来这里买"。python代码
python
import dashscope
from dashscope import TextEmbedding
dashscope.api_key = {YOUR API KEY}
def embed_with_str():
resp = TextEmbedding.call(
model=TextEmbedding.Models.text_embedding_v1,
input='衣服的质量杠杠的,很漂亮,不枉我等了这么久啊,喜欢,以后还来这里买')
print(resp)
if __name__ == '__main__':
embed_with_str()
输出向量结果
到这里对于向量检索服务,以及向量检索服务中常出现的名词也就解释清楚了,更多的向量检索服务相关内容可以去参考官方文档:向量检索服务
开通灵积模型服务
在进行基于向量检索服务与TextEmbedding实现语义搜索之前需要先开通灵积模型服务,进入控制台:https://dashscope.console.aliyun.com/overview?spm=a2c4g.11186623.0.0.31e0697ejMTOD9 点击【去开通】
勾选服务协议点击开通即可。然后点击【管理中心】-【API-KEY管理】,
点击【创建新的API-KEY】
点击复制可以获取API-KEY.
开通向量检索服务
打开向量检索服务控制台:https://dashvector.console.aliyun.com/cn-hangzhou/overview
点击【创建新的API-KEY】,点击复制,
回到向量检索服务首页,点击【创建Cluster】
在打开的页面选择【免费试用】,输入Cluster名称
勾选服务协议,点击【立即开通】完成Cluster的创建
创建完成Cluster之后回到列表页点击Cluster名称进入详情页复制endpoint地址
安装Python
需要下载Python3.7及以上版本,下载地址:https://www.python.org/downloads/windows/
官方网址下载的比较慢,可以直接使用我下载的python
链接:https://pan.baidu.com/s/1vPuGI33mfBOWeEfAWvX0ig?pwd=hk16
提取码:hk16
安装步骤这里不再详细说明,默认安装操作即可 ,安装步骤参考文档:https://docs.python.org/zh-cn/3.11/using/windows.html#installation-steps ,如果选择自定义安装,默认复选框都全部勾选即可。安装完成后进行后面的步骤。
安装环境
python3.11安装成功之后,打开windows cmd窗口,执行如下命令安装环境
python
pip3 install dashvector dashscope
执行结果如图
安装过程出现可能会出现error,报错信息
python
ERROR: Exception:
Traceback (most recent call last):
File "E:\Python311\Lib\site-packages\pip\_vendor\urllib3\response.py", line 437, in _error_catcher
yield
File "E:\Python311\Lib\site-packages\pip\_vendor\urllib3\response.py", line 560, in read
data = self._fp_read(amt) if not fp_closed else b""
^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_vendor\urllib3\response.py", line 526, in _fp_read
return self._fp.read(amt) if amt is not None else self._fp.read()
^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_vendor\cachecontrol\filewrapper.py", line 90, in read
data = self.__fp.read(amt)
^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\http\client.py", line 466, in read
s = self.fp.read(amt)
^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\socket.py", line 706, in readinto
return self._sock.recv_into(b)
^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\ssl.py", line 1278, in recv_into
return self.read(nbytes, buffer)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\ssl.py", line 1134, in read
return self._sslobj.read(len, buffer)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TimeoutError: The read operation timed out
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "E:\Python311\Lib\site-packages\pip\_internal\cli\base_command.py", line 160, in exc_logging_wrapper
status = run_func(*args)
^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\cli\req_command.py", line 247, in wrapper
return func(self, options, args)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\commands\install.py", line 400, in run
requirement_set = resolver.resolve(
^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\resolver.py", line 92, in resolve
result = self._result = resolver.resolve(
^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 481, in resolve
state = resolution.resolve(requirements, max_rounds=max_rounds)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 348, in resolve
self._add_to_criteria(self.state.criteria, r, parent=None)
File "E:\Python311\Lib\site-packages\pip\_vendor\resolvelib\resolvers.py", line 172, in _add_to_criteria
if not criterion.candidates:
File "E:\Python311\Lib\site-packages\pip\_vendor\resolvelib\structs.py", line 151, in __bool__
return bool(self._sequence)
^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\found_candidates.py", line 155, in __bool__
return any(self)
^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\found_candidates.py", line 143, in <genexpr>
return (c for c in iterator if id(c) not in self._incompatible_ids)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\found_candidates.py", line 47, in _iter_built
candidate = func()
^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\factory.py", line 206, in _make_candidate_from_link
self._link_candidate_cache[link] = LinkCandidate(
^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 297, in __init__
super().__init__(
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 162, in __init__
self.dist = self._prepare()
^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 231, in _prepare
dist = self._prepare_distribution()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\resolution\resolvelib\candidates.py", line 308, in _prepare_distribution
return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\operations\prepare.py", line 491, in prepare_linked_requirement
return self._prepare_linked_requirement(req, parallel_builds)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\operations\prepare.py", line 536, in _prepare_linked_requirement
local_file = unpack_url(
^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\operations\prepare.py", line 166, in unpack_url
file = get_http_url(
^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\operations\prepare.py", line 107, in get_http_url
from_path, content_type = download(link, temp_dir.path)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_internal\network\download.py", line 147, in __call__
for chunk in chunks:
File "E:\Python311\Lib\site-packages\pip\_internal\cli\progress_bars.py", line 53, in _rich_progress_bar
for chunk in iterable:
File "E:\Python311\Lib\site-packages\pip\_internal\network\utils.py", line 63, in response_chunks
for chunk in response.raw.stream(
File "E:\Python311\Lib\site-packages\pip\_vendor\urllib3\response.py", line 621, in stream
data = self.read(amt=amt, decode_content=decode_content)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "E:\Python311\Lib\site-packages\pip\_vendor\urllib3\response.py", line 559, in read
with self._error_catcher():
File "E:\Python311\Lib\contextlib.py", line 155, in __exit__
self.gen.throw(typ, value, traceback)
File "E:\Python311\Lib\site-packages\pip\_vendor\urllib3\response.py", line 442, in _error_catcher
raise ReadTimeoutError(self._pool, None, "Read timed out.")
pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.
按照报错后给出的提示执行命令
如果执行pip 升级命令之后还是不行的话,可能是你的网络太差了,建议等待网络比较好的时候尝试。这里透露一下,我的网络可能不好,这一步升级操作尝试了有30分钟。升级成功后如图
升级成功之后再执行安装环境命令
python
pip3 install dashvector dashscope
还是会有报错的可能,那就只能一直尝试了。
20240130补充 ,在当天我尝试了大概几个小时之后一直没有成功,后来有一天早上再次尝试,终于成功了,安装环境命令成功情况如图
准备数据
20240130补充,在执行准备数据命令之前需要先安装git工具
安装Git
git工具下载地址:https://git-scm.com/download/win 如果没有安装git工具,执行git命令会报如下提示
选择需要下载的git工具版本
Git下载完成之后的安装操作可以参考我的博文:https://developer.aliyun.com/article/1431354
Git安装成功后打开【Git CMD】再次执行如下命令获取数据
网络不好的需要多次尝试的
20240201补充 ,多次尝试之后最终成功如图
python
git clone https://github.com/CLUEbenchmark/QBQTC.git
wc -l QBQTC/dataset/train.json
打开Git Bash窗口,输入命令wc -l命令统计行数
通过cat命令查看 数据集中的训练集(train.json)格式如图
从这个数据集中提取title,方便后续进行embedding并构建检索服务,python代码如下
python
import json
def prepare_data(path, size):
with open(path, 'r', encoding='utf-8') as f:
batch_docs = []
for line in f:
batch_docs.append(json.loads(line.strip()))
if len(batch_docs) == size:
yield batch_docs[:]
batch_docs.clear()
if batch_docs:
yield batch_docs
20240201补充 ,保存文件内容到python安装目录为001.py
点击在【此处打开Powershell窗口】,执行001.py文件
通过 DashScope 生成 Embedding 向量
节录官方文档描述:【DashScope灵积模型服务通过标准的API提供了多种模型服务。其中支持文本Embedding的模型中文名为通用文本向量,英文名为text-embedding-v1。我们可以方便的通过DashScope API调用来获得一段输入文本的embedding向量】,python代码如下
python
import dashscope
from dashscope import TextEmbedding
dashscope.api_key='{your-dashscope-api-key}'
def generate_embeddings(text):
rsp = TextEmbedding.call(model=TextEmbedding.Models.text_embedding_v1,
input=text)
embeddings = [record['embedding'] for record in rsp.output['embeddings']]
return embeddings if isinstance(text, list) else embeddings[0]
# 查看下embedding向量的维数,后面使用 DashVector 检索服务时会用到,目前是1536
print(len(generate_embeddings('hello')))
其中,your-dashscope-api-key 即是我们刚才开通灵积模型服务后创建的API-KEY。
将上述python代码保存为002.py文件
替换其中的your-dashscope-api-key为
然后执行002.py文件,执行完成如图
通过 DashVector 构建检索:向量入库
节录官方文档描述:【DashVector 向量检索服务上的数据以集合(Collection)为单位存储,写入向量之前,我们首先需要先创建一个集合来管理数据集。创建集合的时候,需要指定向量维度,这里的每一个输入文本经过DashScope上的text_embedding_v1模型产生的向量,维度统一均为1536。】python代码如下
python
from dashvector import Client, Doc
# 初始化 DashVector client
client = Client(
api_key='{your-dashvector-api-key}',
endpoint='{your-dashvector-cluster-endpoint}'
)
# 指定集合名称和向量维度
rsp = client.create('sample', 1536)
assert rsp
collection = client.get('sample')
assert collection
batch_size = 10
for docs in list(prepare_data('QBQTC/dataset/train.json', batch_size)):
# 批量 embedding
embeddings = generate_embeddings([doc['title'] for doc in docs])
# 批量写入数据
rsp = collection.insert(
[
Doc(id=str(doc['id']), vector=embedding, fields={"title": doc['title']})
for doc, embedding in zip(docs, embeddings)
]
)
assert rsp
其中,your-dashvector-api-key 和 your-dashvector-cluster-endpoint 是向量检索服务创建的。
将上述python代码保存为003.py文件
其中,your-dashvector-api-key 和 your-dashvector-cluster-endpoint 替换为
通过python命令执行003.py文件
语义检索:向量查询
在训练数据集里的title内容都写到DashVector服务上的集合里后,就可以进行快速的向量检索,实现"语义搜索"的能力,基于上面的python代码继续添加如下代码
python
# 基于向量检索的语义搜索
rsp = collection.query(generate_embeddings('应届生 招聘'), output_fields=['title'])
for doc in rsp.output:
print(f"id: {doc.id}, title: {doc.fields['title']}, score: {doc.score}")
到这里整个测评操作就算完成了
操作体验
小建议1
首次操作向量检索服务,整体上操作参考的是官方文档上面进行的,按照文档操作下来整体比较流畅,只是在开通服务时少看了开通向量检索服务获取API-KEY
导致后续又回头重新看了一遍文档,这里希望文档可以着重标识一下,因为最终都是获取API-KEY,所以很容易就会产生第一个开通后获取API-KEY就可以了的误解。
另外就是在上述执行命令时,一些可能的报错情况的处理,文档中没有太多的提及,文档不适合python小白,需要有一定的python操作基础才可以。
建议是否可以提供在线的python3环境方便体验向量检索服务的特性呢?
小建议2
这次体验下来,向量检索服务在整体的性能、易用性、可拓展性等方面都比较出色,检索效率很高。公司业务的话,关于按图搜索商品以及按文本搜索商品场景,现在还是比较常用的,因此接入向量检索服务的话,相信检索效率一定会倍数级提升。但是这里有一点就是,在导入数据集以及构建检索-向量入库的操作都需要通过python操作,虽然有python代码,但是还是有一定的操作成本的。关于这一方面是否可以提供控制台按钮操作或者说是通过导入数据的形式,向量检索服务内部通过python代码将导入的数据自动化构建检索-向量入库,最后提供向量查询呢
小建议3
由于一些图片内容或者商品文本内容在其他云服务器或者是云存储空间,希望可以提供将其他云存储空间图片转换成向量检索服务向量数据的一键式操作方案,或者是其他简便操作,毕竟对于使用方来说,如何生成向量数据并不是重点关心的,使用方主要关注的是如何快速准确的返回检索到的商品数据给用户。就像PolarDB提出的让开发者可以像搭积木一样使用数据库一样,就是说开发者并不关注数据库内部的向量数据转化,而是只关心向量检索服务能否快速准确返回检索结果。