各位老铁,双击666! 今天咱们不聊八卦,来点硬核的!咱们来扒一扒MySQL的Vector Search,看看这玩意儿到底怎么在AI和相似性搜索里搅风搅雨。
开场白:啥是Vector Search?
想象一下,你面前摆着一堆照片,让你找出最像“一只可爱的小猫咪”的那张。 你怎么找? 大概就是凭感觉,看颜色、形状、姿势,然后在脑子里给每张照片打个分。
Vector Search 干的就是类似的事儿。 它先把你的数据(比如那些照片、文本、甚至音乐)变成一堆叫做“向量”的数字。 这些向量在空间里排兵布阵,距离越近的向量,就代表着数据越相似。 这样,你再想找“最像小猫咪”的照片,就变成了在向量空间里找距离最近的向量了。
这比传统数据库的精确匹配可灵活多了! 传统的数据库只能告诉你“有没有完全一样的猫”,而Vector Search能告诉你“哪个最像”。
第一回合:向量化你的数据
要玩Vector Search,第一步就是把你的数据变成向量。 这步叫做“向量化(Embedding)”。
-
文本向量化:
如果你的数据是文本,可以用各种牛逼的模型来向量化,比如:
- Word2Vec: 这老哥比较经典,但对上下文理解不太行。
- GloVe: 也挺好,但现在更流行Transformer-based的模型。
- BERT、RoBERTa、GPT: 这些大佬都是Transformer架构的,理解上下文能力超强!
# Python 代码示例 (使用 sentence-transformers 库) from sentence_transformers import SentenceTransformer model = SentenceTransformer('all-MiniLM-L6-v2') # 选择一个预训练模型 sentences = [ "This is a sentence about cats.", "Here is another sentence about dogs.", "A very cute kitten is playing with a ball of yarn." ] sentence_embeddings = model.encode(sentences) print(sentence_embeddings.shape) # 输出: (3, 384) 表示3个句子,每个句子用384维向量表示 # 打印第一个句子的向量 print(sentence_embeddings[0])
这个代码用
sentence-transformers
库,把三个句子变成向量。all-MiniLM-L6-v2
是个预训练好的模型,可以直接用。 你也可以用自己的数据训练模型,效果更好。 -
图像向量化:
如果你的数据是图像,也可以用预训练好的模型来向量化,比如:
- ResNet: 识别图像中的物体杠杠的。
- VGG: 也很流行,但参数比较多。
- CLIP: OpenAI出品,能同时理解图像和文本,非常强大!
# Python 代码示例 (使用 PyTorch 和 torchvision) import torch import torchvision.models as models from torchvision import transforms from PIL import Image # 加载预训练的 ResNet 模型 model = models.resnet50(pretrained=True) # 移除 ResNet 的最后一层 (全连接层),因为我们只需要特征向量 model = torch.nn.Sequential(*list(model.children())[:-1]) model.eval() # 设置为评估模式 # 定义图像预处理流程 preprocess = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) # 加载图像 image = Image.open("cat.jpg") # 替换为你的图像文件 # 图像预处理 input_tensor = preprocess(image) input_batch = input_tensor.unsqueeze(0) # 添加一个 batch 维度 # 推理 with torch.no_grad(): output = model(input_batch) # 提取特征向量 feature_vector = output.flatten().numpy() print(feature_vector.shape) # 输出: (2048,) 表示图像的特征向量是2048维的 # 打印特征向量 print(feature_vector)
这段代码用PyTorch和ResNet50模型,把一张猫的照片变成2048维的向量。 同样,你也可以用自己的数据训练模型。
-
其他数据向量化:
只要你能把数据变成数字,就能向量化。 比如,音乐可以用频谱分析变成向量,用户行为可以用用户画像变成向量。
第二回合:把向量塞进MySQL
MySQL 8.0.17 之后,开始支持 JSON 数据类型,这为存储向量提供了方便。 但直接用 JSON 存储向量效率不高,所以要用到一些骚操作。
-
方案一:直接存储为 JSON 数组
简单粗暴,但性能较差。
CREATE TABLE vectors ( id INT PRIMARY KEY, vector JSON ); INSERT INTO vectors (id, vector) VALUES (1, '[0.1, 0.2, 0.3, 0.4, 0.5]');
这种方式虽然简单,但是查询效率会很低,不建议在高并发场景下使用。
-
方案二:使用 VARCHAR 存储逗号分隔的数值
稍微好一点,但还是不够高效。
CREATE TABLE vectors ( id INT PRIMARY KEY, vector VARCHAR(255) ); INSERT INTO vectors (id, vector) VALUES (1, '0.1,0.2,0.3,0.4,0.5');
这种方式需要自己解析字符串,性能依然不高。
-
方案三:拆分成多个字段存储
性能最好,但需要预先知道向量的维度。
CREATE TABLE vectors ( id INT PRIMARY KEY, v1 FLOAT, v2 FLOAT, v3 FLOAT, v4 FLOAT, v5 FLOAT ); INSERT INTO vectors (id, v1, v2, v3, v4, v5) VALUES (1, 0.1, 0.2, 0.3, 0.4, 0.5);
这种方式需要预先知道向量的维度,如果向量维度很高,那么表结构会变得非常臃肿。
-
方案四:使用 MySQL 8.0.17+ 的
JSON_ARRAYAGG
和JSON_OBJECT
函数构建向量这种方式结合了 JSON 的灵活性和 MySQL 的查询能力。
-- 假设你有一个临时表,其中包含向量的各个维度 CREATE TEMPORARY TABLE tmp_vector ( dimension INT, value FLOAT ); INSERT INTO tmp_vector (dimension, value) VALUES (1, 0.1), (2, 0.2), (3, 0.3), (4, 0.4), (5, 0.5); -- 使用 JSON_ARRAYAGG 函数构建向量 SELECT JSON_ARRAYAGG(value ORDER BY dimension) AS vector FROM tmp_vector; -- 输出结果: -- ["0.1", "0.2", "0.3", "0.4", "0.5"] -- 使用 JSON_OBJECT 函数构建带有维度信息的向量 SELECT JSON_OBJECTAGG(dimension, value) AS vector FROM tmp_vector; -- 输出结果: -- {"1": "0.1", "2": "0.2", "3": "0.3", "4": "0.4", "5": "0.5"} -- 清理临时表 DROP TEMPORARY TABLE IF EXISTS tmp_vector;
这种方式可以在一定程度上提高查询效率,但是仍然存在一些性能瓶颈。 因为 MySQL 本身对 JSON 的索引支持有限。
第三回合:计算相似度
向量存进去了,下一步就是计算向量之间的相似度了。 常用的相似度算法有:
- 余弦相似度 (Cosine Similarity): 算的是两个向量夹角的余弦值,值越大越相似。 这玩意儿对向量的长度不敏感,只关心方向。
- 欧氏距离 (Euclidean Distance): 算的是两个向量之间的距离,值越小越相似。
- 点积 (Dot Product): 直接把两个向量的元素相乘再加起来,值越大越相似。
在MySQL里,我们可以用SQL语句来计算这些相似度。
-
余弦相似度 (Cosine Similarity):
-- 假设向量存储在表的 v1, v2, v3, v4, v5 字段中 SELECT id, (v1 * query_v1 + v2 * query_v2 + v3 * query_v3 + v4 * query_v4 + v5 * query_v5) / (SQRT(v1 * v1 + v2 * v2 + v3 * v3 + v4 * v4 + v5 * v5) * SQRT(query_v1 * query_v1 + query_v2 * query_v2 + query_v3 * query_v3 + query_v4 * query_v4 + query_v5 * query_v5)) AS cosine_similarity FROM vectors ORDER BY cosine_similarity DESC LIMIT 10;
这段代码计算了向量表中每个向量与查询向量 (
query_v1
,query_v2
,query_v3
,query_v4
,query_v5
) 的余弦相似度,并按降序排列,取出最相似的 10 个结果。 -
欧氏距离 (Euclidean Distance):
SELECT id, SQRT(POW(v1 - query_v1, 2) + POW(v2 - query_v2, 2) + POW(v3 - query_v3, 2) + POW(v4 - query_v4, 2) + POW(v5 - query_v5, 2)) AS euclidean_distance FROM vectors ORDER BY euclidean_distance ASC LIMIT 10;
这段代码计算了向量表中每个向量与查询向量的欧氏距离,并按升序排列,取出距离最近的 10 个结果。
-
点积 (Dot Product):
SELECT id, (v1 * query_v1 + v2 * query_v2 + v3 * query_v3 + v4 * query_v4 + v5 * query_v5) AS dot_product FROM vectors ORDER BY dot_product DESC LIMIT 10;
这段代码计算了向量表中每个向量与查询向量的点积,并按降序排列,取出点积最大的 10 个结果。
注意事项:
- 这些SQL语句都需要手动计算,效率不高。
- 如果向量维度很高,SQL语句会变得非常长,难以维护。
第四回合:优化性能
直接用SQL计算相似度,数据量小还好,数据量一大就GG了。 所以,我们要想办法优化性能。
-
索引:
对向量的某些维度建立索引,可以加速查询。 但对于高维向量,索引的效果可能不明显。
-
近似最近邻 (Approximate Nearest Neighbor, ANN) 算法:
ANN算法可以在牺牲少量精度的前提下,大幅提高查询速度。 常用的ANN算法有:
- HNSW (Hierarchical Navigable Small World): 效果好,速度快,但比较复杂。
- IVF (Inverted File Index): 简单易懂,但效果可能不如HNSW。
虽然MySQL本身没有内置ANN算法,但你可以把向量数据导出到支持ANN的向量数据库里,比如:
- Milvus: 开源的向量数据库,支持多种ANN算法。
- Pinecone: 云上的向量数据库,使用方便。
- Weaviate: 开源的向量搜索引擎,支持GraphQL。
然后,在MySQL里存储向量数据库的ID,用MySQL做一些其他的业务逻辑,用向量数据库做相似性搜索。
-
向量量化 (Vector Quantization):
把向量压缩成更小的表示,可以减少存储空间和计算量。 常用的量化方法有:
- 标量量化 (Scalar Quantization): 把向量的每个元素都量化。
- 乘积量化 (Product Quantization): 把向量分成多个子向量,然后分别量化。
你可以用Python或其他工具对向量进行量化,然后把量化后的向量存到MySQL里。
第五回合:实际应用
Vector Search 在AI和相似性搜索里应用广泛,比如:
- 图像搜索: 搜相似的图片,比如搜“和这张风景照风格类似的图片”。
- 文本搜索: 搜相似的文章,比如搜“和这篇新闻报道内容相关的文章”。
- 推荐系统: 给用户推荐相似的商品或内容,比如“你可能喜欢这些电影”。
- 问答系统: 搜相似的问题,然后给出答案,比如“和这个问题类似的解决方案”。
- 恶意软件检测: 搜相似的恶意代码,然后判断是不是新的恶意软件。
案例分析: 图像搜索
假设我们要建立一个图像搜索系统,让用户可以上传一张图片,然后搜索数据库里相似的图片。
-
准备数据:
收集大量的图片,可以是商品图片、风景图片、人脸图片等等。
-
向量化:
用预训练好的模型(比如ResNet、VGG、CLIP)把每张图片变成向量。
-
存储向量:
把向量存到向量数据库里(比如Milvus、Pinecone)。
-
建立索引:
在向量数据库里建立索引,加速查询。
-
搜索:
用户上传一张图片,我们把这张图片也变成向量,然后在向量数据库里搜索最相似的向量,返回对应的图片。
-
集成:
把向量数据库和你的应用程序集成起来,让用户可以通过你的应用程序搜索图片。
代码示例(Python + Milvus):
from pymilvus import connections, utility, Collection, FieldSchema, DataType, CollectionSchema
from sentence_transformers import SentenceTransformer
from PIL import Image
import torchvision.models as models
from torchvision import transforms
import torch
import numpy as np
# 连接到 Milvus
connections.connect(host="localhost", port="19530")
# 定义集合名称
collection_name = "image_search"
# 如果集合存在,先删除
if utility.has_collection(collection_name):
utility.drop_collection(collection_name)
# 定义字段
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="image_vector", dtype=DataType.FLOAT_VECTOR, dim=2048) #假设ResNet50输出2048维向量
]
# 定义集合 schema
schema = CollectionSchema(
fields=fields,
description="Image search collection"
)
# 创建集合
collection = Collection(collection_name, schema)
# 定义索引参数
index_params = {
"metric_type": "L2", # 欧氏距离
"index_type": "IVF1024", # IVF 索引,1024 个 clusters
"params": {"nlist": 1024}
}
# 创建索引
collection.create_index(field_name="image_vector", index_params=index_params)
# 加载集合到内存
collection.load()
# 定义图像向量化函数
def image_to_vector(image_path):
# 加载预训练的 ResNet 模型
model = models.resnet50(pretrained=True)
# 移除 ResNet 的最后一层 (全连接层),因为我们只需要特征向量
model = torch.nn.Sequential(*list(model.children())[:-1])
model.eval() # 设置为评估模式
# 定义图像预处理流程
preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
# 加载图像
image = Image.open(image_path) # 替换为你的图像文件
# 图像预处理
input_tensor = preprocess(image)
input_batch = input_tensor.unsqueeze(0) # 添加一个 batch 维度
# 推理
with torch.no_grad():
output = model(input_batch)
# 提取特征向量
feature_vector = output.flatten().numpy()
return feature_vector
# 插入数据
# 假设我们有 10 张图片
image_paths = ["cat1.jpg", "cat2.jpg", "dog1.jpg", "dog2.jpg", "bird1.jpg", "bird2.jpg", "tree1.jpg", "tree2.jpg", "house1.jpg", "house2.jpg"] # 替换为你的图片路径
vectors_to_insert = []
for image_path in image_paths:
vector = image_to_vector(image_path)
vectors_to_insert.append(vector)
data = [vectors_to_insert] # Milvus 需要二维数组
collection.insert(data)
# 确保数据已经刷入磁盘
collection.flush()
# 定义搜索参数
search_params = {
"metric_type": "L2", # 欧氏距离
"params": {"nprobe": 16} # 在 16 个 clusters 中搜索
}
# 准备搜索向量
search_vector = image_to_vector("query_cat.jpg") # 替换为你的查询图片路径
search_vectors = [search_vector] # Milvus 需要二维数组
# 执行搜索
results = collection.search(
data=search_vectors,
anns_field="image_vector",
param=search_params,
limit=5, # 返回最相似的 5 个结果
output_fields=["id"] # 返回 id 字段
)
# 打印搜索结果
for hit in results[0]:
print(f"Image ID: {hit.id}, Distance: {hit.distance}")
# 释放集合
collection.release()
这段代码演示了如何使用Milvus和ResNet50模型实现图像搜索。
总结:
MySQL的Vector Search 虽然功能有限,但结合其他工具,也能在AI和相似性搜索中发挥作用。 关键在于:
- 选择合适的向量化模型。
- 选择合适的存储方案。
- 选择合适的相似度算法。
- 想办法优化性能。
希望今天的讲座对大家有所帮助! 各位老铁,下课!