MySQL高阶讲座之:`MySQL`的`Vector Search`:其在`AI`和相似性搜索中的应用。

各位老铁,双击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_ARRAYAGGJSON_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和相似性搜索里应用广泛,比如:

  • 图像搜索: 搜相似的图片,比如搜“和这张风景照风格类似的图片”。
  • 文本搜索: 搜相似的文章,比如搜“和这篇新闻报道内容相关的文章”。
  • 推荐系统: 给用户推荐相似的商品或内容,比如“你可能喜欢这些电影”。
  • 问答系统: 搜相似的问题,然后给出答案,比如“和这个问题类似的解决方案”。
  • 恶意软件检测: 搜相似的恶意代码,然后判断是不是新的恶意软件。

案例分析: 图像搜索

假设我们要建立一个图像搜索系统,让用户可以上传一张图片,然后搜索数据库里相似的图片。

  1. 准备数据:

    收集大量的图片,可以是商品图片、风景图片、人脸图片等等。

  2. 向量化:

    用预训练好的模型(比如ResNet、VGG、CLIP)把每张图片变成向量。

  3. 存储向量:

    把向量存到向量数据库里(比如Milvus、Pinecone)。

  4. 建立索引:

    在向量数据库里建立索引,加速查询。

  5. 搜索:

    用户上传一张图片,我们把这张图片也变成向量,然后在向量数据库里搜索最相似的向量,返回对应的图片。

  6. 集成:

    把向量数据库和你的应用程序集成起来,让用户可以通过你的应用程序搜索图片。

代码示例(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和相似性搜索中发挥作用。 关键在于:

  1. 选择合适的向量化模型。
  2. 选择合适的存储方案。
  3. 选择合适的相似度算法。
  4. 想办法优化性能。

希望今天的讲座对大家有所帮助! 各位老铁,下课!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注