以图搜图-Milvus
事实上这篇文章属于一个大的分类,属于个人的一个愿景。
我一直在寻找属于自己的知识管理方式,其实说起来也非常简单,满足两个基本需求:
- 快速找到需要的内容,包括自己总结过的结构化与非结构化数据。
- 组成知识网络,根据某一个关键点找到关联的知识。
经过较长时间的实践,试过了很多软件,都感觉要将自己的一套归类总结逻辑迁移去出去并被迫改变,同时又没有很好的编程接口。最终对于知识总结和录入方式选择为 Obsidian
,输出结构化 markdown
文本后通过编程完成解析和选择输出模板,同时完成知识提取和展示。
在非结构化数据管理上图像管理也是一个大难题,图像与文本不同,逻辑上的管理还停留在 TAG
或 mate dta
(元数据)的层面,这最大的问题是做图片标签和分类以及元数据管理。这完全是重复性体力劳动,再加上图片存量相当大,对于个人来说几乎是不可能完成的。逻辑分类其实是倾向于准确的逻辑分析归纳式管理,目前似乎只有机器学习通过已有模型来实现。关于这点还在探索中。
图像管理上还有一条路就是以图搜图,以图搜图更像是基于观感上的,图像结构上的图片比较。对于图像风格比对也仅仅是看过文章说明,没有见到过可用的产品或开源项目。这里简单描述一下最近对于结构上以图搜图的探索和时间结果。
TL;DR点击列表跳转
1. Milvus vector database
Vector database for scalable similarity search and AI applications.
Milvus is an open-source vector database built to power embedding similarity search and AI applications. Milvus makes unstructured data search more accessible, and provides a consistent user experience regardless of the deployment environment.
就像上面所介绍的那样,Milvus
是一个矢量数据库,同时提供了相似性搜索以及一些AI应用。
看到了一些熟悉的使用者。
首先瞄一眼架构图大概知道有什么,然后直接去项目 wiki 去看部署方法。
当前最新的稳定版本为 v2.0.2
以及对应的各种语言的 SDK版本。
看了下文件,只有 rpm
包与 deb
,也就是说 redhat/centos
与 debian
系统能直接部署, windwos
与 mac
需要安装 go
语言环境编辑或者使用 docker
了。
windows 10
环境下单机部署
- 安装与配置好
WSL
下载好任意 linux 镜像 - 安装 docker 环境
- 下载
milvus-standalone-docker-compose.yml
并保存为docker-compose.yml
文件 - 使用
docker-compose up -d
启动,使用docker-compose down
停止。
整个过程非常顺滑,就像官方文档写的那样
1 | $ sudo docker-compose ps |
启动了三个容器,分别的 etcd
, minio
以及 milvus
本身。
同时 milvus
监听 19530
接口,似乎使用的 protobuf
通信
1.1 替换存储组件
因为我本地也是使用 minio
做文件管理,自然想直接用本地,不需要再开一个容器
1 | minio: |
然后重新下载一个全新的 minio,结果这次文件由116M变成了92M,还以为下载错误。
将目录指向一个图片目录结果目录识别成桶,但是文件无法识别。重新上传一下发现了问题。之前的 minio 是依赖于文件系统,文件放在桶里面保持原状,改文件系统也会反映出来。新版文件变成同名的文件夹,再下一级是元数据文件和分片文件,将原始文件分成若干小文件,当前文件都不是很大,不确定是否是一般文件管理系统那样的分成多个文件。
所以这导致一个问题就是之前的 minio 存储无法识别,同时由于最新版的 minio 做了文件变更,那就有文件转移和文件备份的问题。之前直接从目录拷出来,这么一改可能要通过接口或命令导出了。
之前的 minio 是文件系统原样管理,很方便直接或三放工具管理。因为就是文件系统的样式。新版的 minio 这么管理对于文件管理工具来说肯定效率更高,但是可能失去了小规模文件管理的优势。
就我个人的使用来说网络访问使用 minio API接口,但是本机直接通过文件系统读取,这样的修改那么即便的是本地也要使用 minio SDK 通过网络访问了。
更严重的是在改了 yaml 文件后竟然报错不识别 minio.enable
和 externalS3
节点,暂时改不了先向后吧
1.2 Attu
Milvus management GUI
attu项目地址 来源于已停止维护的旧UI 管理系统 milvus-insight。
源码下下来发现是 TS
写的,服务端使用 express
框架, 客户端使用 react
。
我想这是因为 Milvus
使用 protobuf
通信,导致通信只能通过流的方式,对于前端普遍使用的 restful json 文本的方式似乎比较困难,同时SDK不知道自是否支持前端环境。
这不是这次的主要问题,先不管。
2. Reverse Image Search
在 Milvus
的文档中,已经给出几种使用的场景,此外官方的另外一个项目 bootcamp ,给出了几个可直接部署使用的项目,而这其中的 Reverse Image Search 子模块就是本次的最终目的。
注意:如果项目无法部署或部署各种报错,尤其是 python 端报错,其中 99% 的原因都是网络原因导致的 python 各种依赖没有安装成功
Reverse Image Search Based on Milvus & Towhee
This demo uses towhee image embedding operator to extract image features by ResNet50, and uses Milvus to build a system that can perform reverse image search.
值得注意的是这个样例在使用了 Milvus
的同时也使用了 towhee
项目用于抽取图像特征值。
先看了一眼源码发现时似乎的 JS ,于是直接下下来编译。于是又发现这个小项目也分为前后端。
- 前端(client):前端使用
react
+TS
的方式开发 - 后端(server):后端使用
python
+uvicorn
开发
再执行各种 npm install
和 pip3 install -r
后迅速跑了起来
结果在扫面路径时发现 python 后端开始下载 pytorch
然后报错,随后在windows 上尝试装 CUDA
与 pytorch
,结果各种原因装不上,于是找了个 linux 环境直接跑容器
如上图所示使用 bootcamp
提供的文件重启容器,发现起了一个 react
的 webclient
一个 python
的 webserver
以及一个 mysql,在容器内修改了 pip 的配置后发现顺畅的安装了 torch
,timm
与 opencv
,这其中 opencv_python
60M,torch
750M,没有镜像这速度可想而知。
下载完成后开始去 github 下载 resnet50
模型文件 resnet50_a1_0-14fe96d1.pth
,这个无法使用 pip 镜像分流,于是立刻换了个上 mac 电脑直接跑前端和 python 后端,手动下载模型后放入用户目录下 torch
的缓存目录。
2.1 启动
当前运行配置
Milvus
为最开始的单机容器配置,milvus-standalone-docker-compose.yml
.- mysql:使用外部配置,在 python 中配置
- python与react 源码运行
python端 server/src/config.py
1 | import os |
- 使用外部数据库必须新建
MYSQL_DB = os.getenv("MYSQL_DB", "milvus")
数据库,数据表milvus_img_search
会在运行时检测并创建 - 连接容器中的
milvus
:MILVUS_HOST = os.getenv("MILVUS_HOST", "127.0.0.1")
,检测网络是否畅通,检测容器所在宿主机防火墙
react 客户端 在 client/src/utils/Endpoint.ts
1 | declare global { |
本地运行不知为何 window._env
没有生效,这里直接改 endpoint
为python服务端地址
先找了一张图发现原图匹配没有问题,随后加入50张图并截图部分匹配发现也可以。
2.2 bootcamp 源码分析
客户端只是起到一个接口转发功能,不做分析
首先粗略看一下这种架构图,并直接上代码
1 | # main.py |
2.2.1 目录扫描
1 | # main.py 调用 operations/load.py ->do_load 方法 |
可以看到核心都在这个 do_load(table_name, image_dir, model, milvus_client, mysql_cli)
方法上
table_name 默认为 mysql 表名 milvus_img_search
,同时也是 milvus
中的集合名
extract_features
:图像特征值提取,这里的参数model
是在main.py
初始化好的src/encode.py
,其中又调用了towhee
使用resnet50
模型milvus_client.insert
:将图片特征值vectors
存入milvus
中的table_name
集合中,并返回记录IDmilvus_client.create_index
:milvus
sdk 方法,创建索引mysql_cli.create_mysql_table
:测试 mysql 是否存在表table_name
,如果不存在则新建。mysql_cli.load_data_to_mysql
:将milvus
的特征值 ID 和图片地址存入 mysql。
看到这里再看上面的架构图,能大致猜到这个 demo 的运行逻辑
- python : 负责特征值提取,通俗来讲就是把图片的特征读取出来,不过这里使用的是
torch
框架个和resnet50
模型(这个模型文件就有97M)。 - milvus:负责存储 python 传过来的图片特征值并返回一个存储ID,这里就体现出
milvus
介绍说的矢量数据库的价值了 - mysql:完全是业务功能,将
milvus
返回的特征值ID和图片文件(路径)相关联。
2.2.3 图片搜索
1 | # main.py |
main.py
中的上传接口
1 | # search.py |
调用到 src/operations/search.py
中的 do_search
model.resnet50_extract_feat
:使用resnet50
模型提取图像特征值milvus_client.search_vectors
:提取特征值后在milvus
做向量计算,实际就是计算向量距离,并返回top_k
个。这里返回的是匹配到的文件特征值和对应的ID;mysql_cli.search_by_milvus_ids
:拿到ID后从 mysql 中查找这个ID,并找到对应的关联图片(就是图片地址)。
这里体现出 milvus
的另一个重要功能,即相似度计算也就是介绍里说的 similarity search
。
其实以图搜图简单的说也就是这两个步骤,找到图片的一些特性信息或者说是指纹。与其他图片的指纹进行比较与计算,并返回相似的程度有多少。当然现实中图片获取本身可能就是非常困难的事情。
2.2.3 测试
既然跑起来了就要对个功能先有个大致的感性的理解,于是就拿数据集内部的图和外部的图片做了些测试。
首先拿数据集的图片计算向量距离为 0
,算是完全匹配,截下来头像距离是 0.7
。
在看到右下角的 空银子
后我想了个办法
如上图左侧为图库中已有的原图,然后找了图库中不存在的,TV版本,小人儿版,绘画风格A,绘画风格B四张图。
感觉上来说原图能识别的人物特色是头发弧度与颜色,那么应该最佳匹配是塑料小人版,其次是绘画风格B。
可以看出TV版没有匹配到,小人版本向量距离为 0.74
最近。不出意料
图3和图4,让人没想到。从结构上与整个观感上来说绘画风格4应该更接近,但是图三反而距离最近,这但是完全没有意料到的。
之后需要改造 python 端并重新设计数据库,并尝试计算现有图库的特征值,并最终用起来。
3. Milvus
看了官方给的例子,反倒是对 Milvus
本身更感兴趣,于是又回去看了 Milvus
的文档。
官方的文档给出的适应方式实际上就是 Reverse Image Search
的应用方式,首先做数据信息提取,这里一般用深度学习。然后 milvus
本身做的是存储与数据对比与匹配工作。
按照官方描述 bootcamp
项目还包括一个问答系统,一个推荐系统,一个视频相似度搜索,一个音频相似度搜索,分子相似度搜索,一个DNS串行分类,一个文本搜索引擎。
看了这些对 milvus
更感兴趣,对这些子项目是如何运行,以及如何与 milvus
交互更感兴趣了。这些项目与我需要的场景有很高的重合度,希望之后能用到。
4. 最后
图片搜索除了 milvus
外还有几个其他框架,比如基于 Apcahe Solr
的 LIRE
实现,这其中有几个已经运行很久且效果不错的开源项目,在看到 milvus
前我对 LIRE
是更感兴趣的。
然后哪个可以最终在个人有限硬件环境中运行,这才是我需要关注的。