如何通过数据库与AI实现以图搜图?OceanBase向量功能详解

OceanBase支持向量数据库的基础能力

当前,数据库存储系统与人工智能技术的结合,可以体现在两个主要的应用方向上。

一、近似搜索。它利用大语言模型(LLM,简称大模型)的嵌入(embedding)技术,将非结构化数据转换为向量数据并存储于数据库系统中。通过数据库系统提供的向量运算和近似度查询功能,实现搜索推荐和非结构化数据查询的应用场景。

二、检索增强生成。大模型具备自然语言对话、文本总结、智能体Agent、辅助编码等通用能力,但限于其预训练时使用有限知识,难以有效应对互联网平台源源不断涌现的海量知识。因此,常见的做法是使用数据库存储问答等语料并为大语言模型提供语料检索,即RAG。

1715577106

在OceanBase社区版的4.3版本中,率先支持了向量数据库的基本能力

  • 支持向量数据类型(VECTOR关键字)定义以及存储;
  • 支持向量数据列创建向量近似邻近搜索(ANN)索引,目前支持IVFFLAT以及HNSW两种算法;
  • 支持分区并行构建向量近似邻近搜索索引;
  • 支持分区并行执行向量近似邻近搜索。

这些能力得以让OceanBase成为上述两种AI应用架构的存储基座,下面按照近似搜索应用架构,以一个简单的图搜图应用来展示OceanBase的向量存储能力。

OceanBase向量存储能力演示

1. 部署OceanBase向量数据库Docker镜像

通过以下命令安装OceanBase向量数据库:

docker run -p 2881:2881 --name obvec -d oceanbase/oceanbase-ce:vector

等待docker容器输出“boot success!”之后,我们可以用SQL接口试玩一下OceanBase的向量处理能力:

obclient [test]> create table t1 (c1 vector(3), c2 int, c3 float, primary key (c2));
Query OK, 0 rows affected (0.128 sec)obclient [test]> insert into t1 values ('[1.1, 2.2, 3.3]', 1, 1.1), ('[  9.1, 3.14, 2.14]', 2, 2.43), ('[7576.42, 467.23, 2913.762]', 3, 54.6), ('[3,1,2]', 4, 4.67), ('[42.4,53.1,5.23]', 5, 423.2), ('[  3.1, 1.5, 2.12]', 6, 32.1), ('[4,6,12]', 7, 23), ('[2.3,66.77,34.35]', 8, 67), ('[0.43,8.342,0.43]', 9, 67), ('[9.99,23.2,5.88]', 10, 67),('[23.5,76.5,6.34]',11,11);
Query OK, 11 rows affected (0.011 sec)
Records: 11  Duplicates: 0  Warnings: 0obclient [test]> CREATE INDEX vidx1_c1_t1  on t1 (c1 l2) using hnsw;
Query OK, 0 rows affected (0.315 sec)obclient [test]> select * from t1;
+--------------------------------------+----+-------+
| c1                                   | c2 | c3    |
+--------------------------------------+----+-------+
| [1.100000,2.200000,3.300000]         |  1 |   1.1 |
| [9.100000,3.140000,2.140000]         |  2 |  2.43 |
| [7576.419922,467.230011,2913.761963] |  3 |  54.6 |
| [3.000000,1.000000,2.000000]         |  4 |  4.67 |
| [42.400002,53.099998,5.230000]       |  5 | 423.2 |
| [3.100000,1.500000,2.120000]         |  6 |  32.1 |
| [4.000000,6.000000,12.000000]        |  7 |    23 |
| [2.300000,66.769997,34.349998]       |  8 |    67 |
| [0.430000,8.342000,0.430000]         |  9 |    67 |
| [9.990000,23.200001,5.880000]        | 10 |    67 |
| [23.500000,76.500000,6.340000]       | 11 |    11 |
+--------------------------------------+----+-------+
11 rows in set (0.004 sec)obclient [test]> select c1,c2 from t1 order by c1 <-> '[3,1,2]' limit 2;
+------------------------------+----+
| c1                           | c2 |
+------------------------------+----+
| [3.000000,1.000000,2.000000] |  4 |
| [3.100000,1.500000,2.120000] |  6 |
+------------------------------+----+
2 rows in set (0.013 sec)
  • 首先创建一个包含向量列c1的向量数据表t1;
  • 插入向量数据,展示OceanBase向量数据常量值的定义方式;
  • 在该向量数据表上创建hnsw向量索引(也支持创建ivfflat索引);
  • 向量数据表全表扫描;
  • 一个典型的向量近似最邻近查询(select XXX from XX order by XXX limit XX);
    • <->:计算向量之间的欧式距离;
    • <@>:计算向量之间的内积;
    • <~>:计算向量之间的cosine距离。

2. 处理图片数据

可以选择任意的分类图片库作为数据集,本文演示资料是从如下链接下载:

极市开发者平台-计算机视觉算法开发落地平台-极市科技

这个图片库中的图片大小不一,对于传统的机器学习应用来说需要统一图片大小,不过我们使用embedding模型进行向量搜索的方式并不需要。唯一需要做的预处理操作是:图片库中的图片按照图片目录事先已做好归类,需要打散统一放到一个目录下:

import os
import shutildef copy_imgs(src, dest):if not os.path.exists(dest):os.makedirs(dest)for root, dirs, files in os.walk(src):for file in files:if file.endswith('.jpg'):src_file_path = os.path.join(root, file)dest_file_path = os.path.join(dest, file)shutil.copy2(src_file_path, dest_file_path)copy_imgs(src_dir, dest_dir)

3. 使用Python连接OceanBase

我们使用sqlalchemy库连接OceanBase,由于vector类型并不是mysql方言中支持的类型,需要定义一个Vector类,实现该类型从数据库类型转为python列表类型、列表类型转为OceanBase向量常量类型的方法:

# OceanBase Vector DataBase 
import datetime
from typing import Any, Callable, Iterable, List, Optional, Sequence, Tuple, Typefrom sqlalchemy import Column, String, Table, create_engine, insert, text
from sqlalchemy.types import UserDefinedType, Float, String
from sqlalchemy.dialects.mysql import JSON, LONGTEXT, VARCHAR, INTEGERtry:from sqlalchemy.orm import declarative_base
except ImportError:from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()def from_db(value):return [float(v) for v in value[1:-1].split(',')]def to_db(value, dim=None):if value is None:return valuereturn '[' + ','.join([str(float(v)) for v in value]) + ']'class Vector(UserDefinedType):cache_ok = True_string = String()def __init__(self, dim):super(UserDefinedType, self).__init__()self.dim = dimdef get_col_spec(self, **kw):return "VECTOR(%d)" % self.dimdef bind_processor(self, dialect):def process(value):return to_db(value, self.dim)return processdef literal_processor(self, dialect):string_literal_processor = self._string._cached_literal_processor(dialect)def process(value):return string_literal_processor(to_db(value, self.dim))return processdef result_processor(self, dialect, coltype):def process(value):return from_db(value)return process# 与 OceanBase Vector DataBase 建立连接
ob_host = "127.0.0.1"
ob_port = 2881
ob_database = "test"
ob_user = "root@test"
ob_password = ""
connection_str = f"mysql+pymysql://{ob_user}:{ob_password}@{ob_host}:{ob_port}/{ob_database}?charset=utf8mb4"
ob_vector_db = create_engine(connection_str)

OceanBase docker启动会自动创建一个test租户,并在本地2881端口开启MySQL服务,构造连接串后创建连接即可。

4. 定义向量处理接口

接着我们事先定义好OceanBase向量处理的Python接口:

# 创建 img2img 表
def ob_create_img2img(embedding_dim):img2img_table_query = f"""CREATE TABLE IF NOT EXISTS `img2img` (id INT NOT NULL, embedding VECTOR({embedding_dim}), path VARCHAR(1024) NOT NULL, PRIMARY KEY (id))"""with ob_vector_db.connect() as conn:with conn.begin():conn.execute(text(img2img_table_query))print(f"create table ok: {img2img_table_query}")glb_img_id = 0
# 向 img2img 表中插入向量
def ob_insert_img2img(embedding_dim, embedding_vec, path):global glb_img_idglb_img_id += 1img_id = glb_img_idimg2img_table = Table("img2img",Base.metadata,Column("id", INTEGER, primary_key=True),Column("embedding", Vector(embedding_dim)),Column("path", VARCHAR(1024), nullable=False),keep_existing=True,)data = [{"id": img_id,"embedding": embedding_vec.tolist(),"path": path,}]with ob_vector_db.connect() as conn:with conn.begin():conn.execute(insert(img2img_table).values(data))# vector_distance_op:
# <->: 欧式距离; <~>: cosine距离; <@>: 点积 
# 使用 OceanBase Vector DataBase 进行 ANN 查找
def ob_ann_search(vector_distance_op, query_vector, topk):try:from sqlalchemy.engine import Rowexcept ImportError:raise ImportError("Could not import Row from sqlalchemy.engine. ""Please 'pip install sqlalchemy>=1.4'.")vector_str = to_db(query_vector)sql_query = f"""SELECT path, embedding {vector_distance_op} '{vector_str}' as distanceFROM `img2img`ORDER BY embedding {vector_distance_op} '{vector_str}'LIMIT {topk}"""sql_query_str_for_print = f"""SELECT path, embedding {vector_distance_op} '?' as distanceFROM `img2img`ORDER BY embedding {vector_distance_op} '?'LIMIT {topk}"""with ob_vector_db.connect() as conn:begin_ts = datetime.datetime.now()results: Sequence[Row] = conn.execute(text(sql_query)).fetchall()print(f"Search {sql_query_str_for_print} cost: {(datetime.datetime.now() - begin_ts).total_seconds()} s")return [res for res in results]return []
  • ob_create_img2img:创建向量数据表。需要传入向量维度,目前OceanBase限制一个向量数据表只支持插入固定维度的向量;在图搜图这个应用中,表的schema定义为:
    • id:每一张图片分配一个唯一的id号,用作向量数据的主键;
    • embedding:存放图片嵌入的向量数据,用于近似查询;
    • path:图片的路径。通过embedding字段找到近似的向量后,利用path字段来展示图片。
  • ob_insert_img2img:向向量数据表插入向量数据;
  • ob_ann_search:用于执行向量近似最邻近查询并计算查询耗时。

5. 图片导入OceanBase

我们使用CLIP模型来将图片转为向量。CLIP模型可以使用towhee库进行下载:

import os
import shutil
from towhee import ops,pipe,AutoPipes,AutoConfig,DataCollectionimg_pipe = AutoPipes.pipeline('text_image_embedding')

然后,简单调用一下即可获取向量:

def img_embedding(path):return img_pipe(path).get()[0]

最后将整个pipeline组合起来,将图片库中的所有图片转为向量,再插入OceanBase中。特殊处理一下第一次插入,需要额外执行向量数据表的创建:

# 将图片转换为 embedding 向量后导入 OceanBase Vector DataBase
def import_all_imgs(img_dir):embedding_dim = -1first_embedding = Trueimgs = os.listdir(img_dir)for i in range(len(imgs)):path = os.path.join(img_dir, imgs[i])vec = img_embedding(path)if first_embedding:embedding_dim = len(vec.tolist())ob_create_img2img(embedding_dim)first_embedding = Falseif embedding_dim != len(vec.tolist()):print(f"dim mismatch!! ---- expect: {embedding_dim} while get {len(vec.tolist())}")breakob_insert_img2img(embedding_dim, vec, path)if i % 100 == 0:print(f"{i} vectors inserted...")print("import finish")dest_dir = "/your/image/dest/dir"
import_all_imgs(dest_dir)

导入完成后,开启一个MySQL连接,可以看到导入了5399条维度为512的向量:

obclient [test]> show create table img2img\G
*************************** 1. row ***************************Table: img2img
Create Table: CREATE TABLE `img2img` (`id` int(11) NOT NULL,`embedding` vector(512) DEFAULT NULL,`path` varchar(1024) NOT NULL,PRIMARY KEY (`id`),KEY `vidx` (`embedding`) BLOCK_SIZE 16384 LOCAL
) DEFAULT CHARSET = utf8mb4 ROW_FORMAT = DYNAMIC COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 1 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0
1 row in set (0.002 sec)obclient [test]> select count(*) from img2img;
+----------+
| count(*) |
+----------+
|     5399 |
+----------+
1 row in set (0.021 sec)

6. 启动图搜图小应用

我们使用gradio库作为简易的WebUI,接受以下两个输入。

  • 图片上传组件:待查询的图片。
  • topK滑动组件:设定最邻近查询的topK值。

查询时,首先将传入的图片写到临时路径,再将临时图片嵌入为向量,最后调用之前定义的ob_ann_search函数获取最邻近图片路径列表。最后通过gallery组件展示图片:

# Gradio 界面
import gradio as gr
import os
import IPython.display as display
import imageio# Gradio界面的主要逻辑函数
def show_search_results(image, topk):if not os.path.exists("uploads"):os.makedirs("uploads")# 保存上传的图片到临时目录并获取其路径if image is not None:temp_image_path = os.path.join("uploads", "uploaded_image.jpg")imageio.imsave(temp_image_path, image)# 调用图搜索函数query_vec = img_embedding(temp_image_path)res = ob_ann_search("<~>", query_vec, topk)result_paths = [r.path for r in res]return result_pathsreturn []# 创建Gradio UI
iface = gr.Interface(fn=show_search_results,inputs=[gr.Image(label="上传图片"), gr.Slider(1, 10, step=1, label="Top K")],outputs=gr.Gallery(label="搜索结果图片"),examples=[])# 在Jupyter Notebook内运行Gradio应用
iface.launch()

简单测试一下,可以发现原图被精确地查找了出来:

1715577126

而相关图片中都是“海豹”!

1715577132

7. 创建一个向量索引会如何?

开启一个MySQL连接,创建一个ivfflat索引:

obclient [test]> create index vidx on img2img (embedding l2) using ivfflat;
Query OK, 0 rows affected (11.129 sec)

创建索引前耗时39ms,而在利用向量索引进行查询优化后,仅7.6ms就响应了Top 9的结果:

1715577147

1715577153

拥抱AI,强化向量功能

OceanBase分布式数据库-海量数据 笔笔算数

  • 依靠OceanBase的分布式存储引擎,提供海量向量数据存储能力;
  • 扩展OceanBase分区并行执行能力,提供高效的向量近似检索能力。

这意味着在海量存储和高效检索这两个场景下,OceanBase用户将获得更低的存储成本、更快的查询速度和更精确的查询结果

此外,在AI应用中,相对于直接存储非结构化数据,存储非结构化数据Embedding后的向量数据具有两个优势:其一,数据更安全,非结构化数据对于数据库管理者不可见;其二,便于语义理解,向量近似搜索是一种从语义层面的检索方式,相似的文字、图片、视频信息具有距离接近的向量数值,这意味着用户在检索时更加灵活,即使使用相近的关键词也能得到精确的检索结果,节省检索成本并提高检索效率。

目前OceanBase可以初步支持近似搜索和搜索增强两个典型应用场景。

近似搜索场景包括但不限于:

  • 搜索推荐;
  • 数据分类、去重;
  • 用于生成式模型的向量输入,如风格迁移应用;
  • ……

检索增强生成场景包括但不限于:

  • 私有知识库问答;
  • Text2SQL;
  • ……

后续OceanBase将继续强化向量功能,包括但不限于简化ANN搜索的SQL语法、支持GPU加速、支持更多向量操作函数及向量检索算法、强化标量向量混合查询能力,以及提供更多的AI接口,比如支持matrix,能够直接使用SQL接口对图片进行一些变换操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/352755.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决外网404:清除DNS缓存并配置host主机使用知名公共DNS服务

在 Windows 上清除/刷新 DNS 缓存 对于所有Windows版本&#xff0c;清除DNS缓存的过程都是相同的。你需要使用管理员权限打开命令提示符并运行ipconfig /flushdns。 浏览器清除DNS缓存 大多数现代的Web浏览器都有一个内置的DNS客户端&#xff0c;以防止每次访问该网站时…

防止Selenium被检测 Google Chrome 125

背景 最近在使用selenium自动播放学习课程&#xff0c;相信大家也有一些类似的使用场景。 能自动化的事情&#xff0c;绝不自己干。 为防止被检测是机器人做题&#xff0c;刷视频&#xff0c;需要做一些小调整。 先来看作为服务方维护者&#xff0c;是如何检测是Selenium打…

深度神经网络修复策略综述

源自&#xff1a;软件学报 作者&#xff1a;梁震, 刘万伟, 吴陶然, 薛白, 王戟, 杨文婧 注&#xff1a;若出现无法显示完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整文章 摘 要 随着智能信息时代的发展, 深度神经网络在人类社会众多领域中的应用, 尤其是…

鸿蒙开发实战:灵活定制Tabs组件,实现个性化页签布局

闪客 沉默的闪客 2024-06-16 20:01 陕西 大家好&#xff0c;又一个项目已经基本完成 是一个元服务英语单词卡片项目&#xff0c;后面一步一步的进行分析拆解&#xff0c;今天来实现一个Tabs组件自定义界面开发。 鸿蒙ArkUI 开发的时候&#xff0c;Tabs 组件很常用&#xff0c;…

Vue项目中实现骨架占位效果-demo

创建组件 Skeleton.vue <template><div class"skeleton"><div class"skeleton-item" v-for"n in count" :key"n"></div></div> </template><script> export default {props: {count: {ty…

基于Matlab的细胞计数图像处理系统(GUI界面有报告) 【含Matlab源码 MX_003期】

简介&#xff1a; 本文旨在解决生物血细胞数目统计的挑战&#xff0c;提出了基于图像处理的综合方案。通过MATLAB平台&#xff0c;我们设计并实现了一套完整的细胞图像处理与分析流程。在预处理阶段&#xff0c;采用图像增强和阈值分割等方法&#xff0c;有效地提高了细胞图像的…

大型语言模型在AMD GPU上的推理优化

Large language model inference optimizations on AMD GPUs — ROCm Blogs 大型语言模型&#xff08;LLMs&#xff09;已经改变了自然语言处理和理解&#xff0c;促进了在多个领域中的众多人工智能应用。LLMs在包括AI助手、聊天机器人、编程、游戏、学习、搜索和推荐系统在内的…

这三款使用的视频、图片设计工具,提供工作效率

Videograp Videograp是一款专注于视频生成的工具&#xff0c;特别适合需要快速剪辑和编辑视频的用户。Videograp具备以下特点&#xff1a; 影音比例转换&#xff1a;Videograp支持调整视频的分辨率和比例&#xff0c;使其更适合不同的播放环境和设备。 AI快剪&#xff1a;该工…

Einops 张量操作快速入门

张量&#xff0c;即多维数组&#xff0c;是现代机器学习框架的支柱。操纵这些张量可能会变得冗长且难以阅读&#xff0c;尤其是在处理高维数据时。Einops 使用简洁的符号简化了这些操作。 Einops &#xff08;Einstein-Inspired Notation for operations&#xff09;&#xff…

第二篇: 掌握Docker的艺术:深入理解镜像、容器和仓库

掌握Docker的艺术&#xff1a;深入理解镜像、容器和仓库 1. 引言 1.1 简要介绍Docker的重要性 在当今快速发展的技术世界中&#xff0c;软件开发和部署的效率和可靠性是衡量成功的关键因素。Docker&#xff0c;作为一个开源的容器化平台&#xff0c;革新了软件的打包、分发和…

电致变色和电泳——有什么区别?

虽然电泳显示器和电致变色显示器都是反射显示器的示例&#xff0c;但其基础技术却截然不同。电致变色显示器采用超薄聚合物&#xff0c;可响应施加的电场而改变颜色。电场使电致变色材料发生化学氧化和还原。这种变化需要的能量很少&#xff0c;而且比较稳定&#xff0c;因此刷…

PostgreSQL性能优化之分区表 #PG培训

在处理大规模数据时&#xff0c;PostgreSQL的性能优化是一个非常重要的话题&#xff0c;其中分区表&#xff08;Partitioned Tables&#xff09;是提高查询和数据管理效率的重要手段。本文将详细介绍PostgreSQL分区表的概念、优势、创建与管理方法以及一些常见的优化策略。 #P…

课程设计——基于FPGA的交通红绿灯控制系统(源代码)

摘要&#xff1a; 本课程设计旨在设计一个基于FPGA&#xff08;现场可编程门阵列&#xff09;的交通红绿灯控制系统。该系统模拟了实际道路交叉口的红绿灯工作场景&#xff0c;通过硬件描述语言&#xff08;如Verilog或VHDL&#xff09;编写源代码实现。系统包含三个主要部分&a…

Servlet快速入门

Servlet Servlet(server applet)是运行在服务端(tomcat)的Java小程序,是sun公司提供的一套定义动态资源的规范,从代码层面讲servlet就是一个接口.用来接收-处理客户端请求,响应给浏览器的动态资源.在整个Web应用中,Servlet主要负责接收处理请求,协同调度功能以及响应数据,可以将…

数据结构-十大排序算法集合(四万字精讲集合)

前言 1&#xff0c;数据结构排序篇章是一个大的工程&#xff0c;这里是一个总结篇章&#xff0c;配备动图和过程详解&#xff0c;从难到易逐步解析。 2&#xff0c;这里我们详细分析几个具备教学意义和实际使用意义的排序&#xff1a; 冒泡排序&#xff0c;选择排序&#xff0c…

算法体系-19 第十九节 暴力递归到动态规划

一 动画规划的概念 优化出现重复解的递归 一旦写出递归来&#xff0c;改动态规划就很快 尝试策略和状态转移方程是一码事 学会尝试是攻克动态规划最本质的能力 如果你发现你有重复调用的过程&#xff0c;动态规划在算过一次之后把答案记下来&#xff0c;下回在越到重复调用过程…

知网G4期刊《中华活页文选》投稿指南//收稿方向

知网G4期刊《中华活页文选》投稿指南//收稿方向 中华活页文选&#xff08;教师版&#xff09;、中华活页文选&#xff08;传统文化教学与研究&#xff09; 知网&#xff0c; G4 国家级 收稿方向&#xff1a;中华活页文选&#xff08;教师版&#xff09;&#xff1a;中小学学段…

Python基础语法学习(工程向)-Stage1

输出的方式&#xff1a; print(fabscwdasd {num}) print(asbduwiu %d, a) print(asnidoian %d %d %d,a,b,c)不换行 print(asbdiuabw,end )输入 a input(输入) 只能输入字符串形式&#xff0c;如果相当做数字用则将其转化为数字 只有合法的数字才能转化成功 a int(input()…

JVM常用概念之扁平化堆容器

扁平化堆容器是OpenJDK Valhalla 项目提出的&#xff0c;其主要目标为将值对象扁平化到其堆容器中&#xff0c;同时支持这些容器的所有指定行为&#xff0c;从而达到不影响原有功能的情况下&#xff0c;显著减少内存空间的占用&#xff08;理想条件下可以减少24倍&#xff09;。…

充电学习—3、Uevent机制和其在android层的实现

sysfs 是 Linux userspace 和 kernel 进行交互的一个媒介。通过 sysfs&#xff0c;userspace 可以主动去读写 kernel 的一些数据&#xff0c;同样的&#xff0c; kernel 也可以主动将一些“变化”告知给 userspace。也就是说&#xff0c;通过sysfs&#xff0c;userspace 和 ker…