本文 的 原文 地址
本文 的 原文 地址
尼恩:LLM大模型学习圣经PDF的起源
在40岁老架构师 尼恩的读者交流群(50+)中,经常性的指导小伙伴们改造简历。
经过尼恩的改造之后,很多小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试机会,拿到了大厂机会。
然而,其中一个成功案例,是一个9年经验 网易的小伙伴,当时拿到了一个年薪近80W的大模型架构offer,逆涨50%,那是在去年2023年的 5月。
- 惊天大逆袭:8年小伙20天时间提75W年薪offer,逆涨50%,秘诀在这
不到1年,小伙伴也在团队站稳了脚跟,成为了名副其实的大模型 应用 架构师。接下来,尼恩架构团队,通过 梳理一个《LLM大模型学习圣经》 帮助更多的人做LLM架构,拿到年薪100W, 这个内容体系包括下面的内容:
- 《Python学习圣经:从0到1精通Python,打好AI基础》
- 《LLM大模型学习圣经:从0到1吃透Transformer技术底座》
- 《LangChain学习圣经:从0到1精通LLM大模型应用开发的基础框架》
- 《LLM大模型学习圣经:从0到1精通RAG架构,基于LLM+RAG构建生产级企业知识库》
- 《SpringCloud + Python 混合微服务架构,打造AI分布式业务应用的技术底层》
- 《LLM大模型学习圣经:从0到1吃透大模型的顶级架构》
- 《LLM 智能体 学习圣经:从0到1吃透 LLM 智能体 的架构 与实操》
- 《LLM 智能体 学习圣经:从0到1吃透 LLM 智能体 的 中台 架构 与实操》
- 《Spring 集成 DeepSeek 的 3大方法,史上最全》
- 《基于Dify +Ollama+ Qwen2 完成本地 LLM 大模型应用实战》
- 《Spring AI 学习圣经 和配套视频 》
- 《Text2SQL圣经:从0到1精通Text2Sql(Chat2Sql)的原理,以及Text2Sql开源项目的使用》
- 《AI部署架构:A100、H100、A800、H800、H20的差异以及如何选型?开发、测试、生产环境如何进行部署架构?》
- 生产环境 K8S + Deepseek 实现大模型部署 和 容器调度(图解+史上最全)
- 《最近大火的 MCP 协议,看这篇文章就够了》
- 《美团面试:LLM 大模型会有 什么问题?说说进行 RAG 优化的方法?》
常用的向量数据库
向量数据库, 是一种专门用于存储和查询向量数据的数据库。
向量数据, 也就是embedding向量。
向量数据, 典型结构是一个 一维教组 ,教组 中的元素是数值(通常是浮点数)。
这些数值, 表示对象或数据 在多维空间中的 位置、特征或属性。
向量数据库的主要功能
- 管理:向量数据库以原始数据形式处理数据,能够有效地组织和管理数据,便于AI模型应用。
- 存储:能够存储向量数据,包括各种AI模型需要使用到的高维数据。
- 检索:向量数据库特别擅长高效地检索数据,这一个特点能够确保AI模型在需要的时候快速获得所需的数据。这也是向量数据库能够在一些推荐系统或者检索系统中得到应用的重要原因。
向量数据库的主要优点是,它允许基于数据的向量距离或相似性进行快速准确的相似性搜索和检索。
这意味着,可以使用向量数据库,根据其语义或上下文含义查找最相似或最相关的数据,而不是使用基于精确匹配或预定义标准 查询数据库 的传统方法。
向量数据库可以搜索非结构化数据,但也可以处理半结构化甚至结构化数据。例如,可以使用向量数据库执行以下操作,根据视觉内容和风格查找与给定图像相似的图像,根据主题和情感查找与给定文档相似的文档,以及根据功能和评级查找与给定产品相似的产品。
2023 向量数据排名
常用的向量数据库 对比
维度ElasticsearchMilvusPineconeFAISSChromaPGVectorWeaviateQdrant架构分布式,多节点分布式,云原生全托管 Serverless单机库单机/轻量集群PostgreSQL 扩展分布式(实验性)分布式,云原生索引算法HNSW, IVFIVF/HNSW/DiskANN自动优化IVF/PQ/HNSWHNSWIVFFlat, HNSWHNSW, IVFHNSW, DiskANN扩展性高(分片与副本)极高(动态扩缩容)自动扩展需手动分片低依赖 PostgreSQL中(分片支持)高(自动分片)部署复杂度中等(需集群管理)高(需 K8s 运维)无需部署低(仅库集成)极低低(PG 扩展)中等(模块配置)中等(需 Rust 生态)查询性能中等(百万级 ms 级)高(十亿级 要创建的集合必须包含一个主键字段和一个向量字段。主键字段支持INT64和VarChar数据类型。首先,准备必要的参数,包括字段模式、集合模式和集合名称。在定义集合模式之前,为集合中的每个字段创建一个模式。为了减少数据插入中的复杂性,Milvus允许您为每个标量字段指定一个默认值(除主键字段外)。这意味着,如果您在插入数据时将某个字段留空,将使用在字段模式创建期间配置的默认值。[/code]from pymilvus import CollectionSchema, FieldSchema, DataType
book_id = FieldSchema(
name="book_id",
dtype=DataType.INT64,
is_primary=True,
)
book_name = FieldSchema(
name="book_name",
dtype=DataType.VARCHAR,
max_length=200,
default_value="Unknown" # 默认值为"Unknown"
)
word_count = FieldSchema(
name="word_count",
dtype=DataType.INT64,
default_value=9999 # 默认值为9999
)
book_intro = FieldSchema(
name="book_intro",
dtype=DataType.FLOAT_VECTOR,
dim=2
)
schema = CollectionSchema(
fields=[book_id, book_name, word_count, book_intro],
description="Test book search", # 描述为"测试图书搜索"
enable_dynamic_field=True # 启用动态模式
)
collection_name = "book"
from pymilvus import Collection
collection = Collection(
name=collection_name,
schema=schema,
using='default',
shards_num=2
)- wget https://github.com/milvus-io/milvus/releases/download/v2.3.0/milvus-standalone-docker-compose.yml -O docker-compose.yml
复制代码 from pymilvus import Collection
collection = Collection(
name=collection_name,
schema=schema,
using='default',
shards_num=2
)- sudo docker compose up -d
复制代码 修改集合
目前,TTL功能仅在Python中可用。- Creating milvus-etcd ... done
- Creating milvus-minio ... done
- Creating milvus-standalone ... done
复制代码 上面的示例将集合的TTL更改为1800秒。
检查集合是否存在
验证集合是否存在于Milvus中。检查集合详情
- Name Command State Ports
- --------------------------------------------------------------------------------------------------------------------
- milvus-etcd etcd -advertise-client-url ... Up 2379/tcp, 2380/tcp
- milvus-minio /usr/bin/docker-entrypoint ... Up (healthy) 9000/tcp
- milvus-standalone /tini -- milvus run standalone Up 0.0.0.0:19530->19530/tcp, 0.0.0.0:9091->9091/tcp
复制代码 列出所有集合
- docker port milvus-standalone 19530/tcp
复制代码 删除集合
创建集合别名
删除集合别名
- apiVersion: v1
- kind: Namespace
- metadata:
- name: milvus
复制代码 修改集合别名
将现有的别名更改为另一个集合。
以下示例基于别名 publication 最初是为另一个集合创建的情况。- // etcd StatefulSet
- apiVersion: apps/v1
- kind: StatefulSet
- metadata:
- name: etcd
- namespace: milvus
- spec:
- serviceName: etcd
- replicas: 3
- selector:
- matchLabels:
- app: etcd
- template:
- metadata:
- labels:
- app: etcd
- spec:
- containers:
- - name: etcd
- image: quay.io/coreos/etcd:v3.5.7
- env:
- - name: ETCD_NAME
- valueFrom:
- fieldRef:
- fieldPath: metadata.name
- - name: ETCD_DATA_DIR
- value: /var/lib/etcd
- - name: ETCD_INITIAL_CLUSTER
- value: "etcd-0=http://etcd-0.etcd:2380,etcd-1=http://etcd-1.etcd:2380,etcd-2=http://etcd-2.etcd:2380"
- - name: ETCD_INITIAL_ADVERTISE_PEER_URLS
- value: "http://$(POD_NAME).etcd:2380"
- - name: ETCD_LISTEN_PEER_URLS
- value: "http://0.0.0.0:2380"
- - name: ETCD_LISTEN_CLIENT_URLS
- value: "http://0.0.0.0:2379"
- - name: ETCD_ADVERTISE_CLIENT_URLS
- value: "http://$(POD_NAME).etcd:2379"
- ports:
- - containerPort: 2379
- name: client
- - containerPort: 2380
- name: peer
- volumeMounts:
- - name: etcd-data
- mountPath: /var/lib/etcd
- volumeClaimTemplates:
- - metadata:
- name: etcd-data
- spec:
- accessModes: ["ReadWriteOnce"]
- storageClassName: "standard"
- resources:
- requests:
- storage: 10Gi
- ---
- // etcd Service
- apiVersion: v1
- kind: Service
- metadata:
- name: etcd
- namespace: milvus
- spec:
- clusterIP: None
- ports:
- - port: 2379
- name: client
- - port: 2380
- name: peer
- selector:
- app: etcd
复制代码 加载集合
在进行搜索或查询之前如何将集合加载到内存中。在Milvus中,所有搜索和查询操作都在内存中执行。
Milvus允许用户将集合加载为多个副本,以利用额外的查询节点的CPU和内存资源。这个功能提高了整体的QPS和吞吐量,而无需额外的硬件。在加载集合之前,请确保您已经为其创建了索引。- // MinIO Deployment
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: minio
- namespace: milvus
- spec:
- replicas: 1 # 生产环境建议 4 节点分布式部署
- selector:
- matchLabels:
- app: minio
- template:
- metadata:
- labels:
- app: minio
- spec:
- containers:
- - name: minio
- image: minio/minio:RELEASE.2023-09-04T19-57-37Z
- args:
- - server
- - /data
- env:
- - name: MINIO_ROOT_USER
- value: "minioadmin"
- - name: MINIO_ROOT_PASSWORD
- value: "minioadmin"
- ports:
- - containerPort: 9000
- - containerPort: 9001
- volumeMounts:
- - name: minio-data
- mountPath: /data
- volumes:
- - name: minio-data
- persistentVolumeClaim:
- claimName: minio-pvc
- ---
- //MinIO PVC
- apiVersion: v1
- kind: PersistentVolumeClaim
- metadata:
- name: minio-pvc
- namespace: milvus
- spec:
- accessModes:
- - ReadWriteOnce
- storageClassName: "standard"
- resources:
- requests:
- storage: 50Gi
- ---
- // MinIO Service
- apiVersion: v1
- kind: Service
- metadata:
- name: minio
- namespace: milvus
- spec:
- ports:
- - port: 9000
- targetPort: 9000
- - port: 9001
- targetPort: 9001
- selector:
- app: minio
复制代码 释放集合
如何在搜索或查询后释放集合以减少内存使用。- // Milvus Proxy Service (API 入口)
- apiVersion: v1
- kind: Service
- metadata:
- name: milvus-proxy
- namespace: milvus
- spec:
- type: NodePort # 生产环境建议 LoadBalancer 或 Ingress
- ports:
- - port: 19530
- targetPort: 19530
- nodePort: 30001
- selector:
- app: milvus-proxy
- ---
- // Milvus 组件 Deployment(示例:Proxy、Data Node、Query Node)
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- name: milvus-proxy
- namespace: milvus
- spec:
- replicas: 2
- selector:
- matchLabels:
- app: milvus-proxy
- template:
- metadata:
- labels:
- app: milvus-proxy
- spec:
- containers:
- - name: proxy
- image: milvusdb/milvus:v2.3.3
- command: ["milvus", "run", "proxy"]
- env:
- - name: ETCD_ENDPOINTS
- value: "etcd-0.etcd:2379,etcd-1.etcd:2379,etcd-2.etcd:2379"
- - name: MINIO_ADDRESS
- value: "minio:9000"
- - name: MINIO_ACCESS_KEY
- value: "minioadmin"
- - name: MINIO_SECRET_KEY
- value: "minioadmin"
- ports:
- - containerPort: 19530
- resources:
- requests:
- memory: "2Gi"
- cpu: "1"
- limits:
- memory: "4Gi"
- cpu: "2"
- // Data Node、Query Node、Index Node 类似,需分别部署
复制代码 向量索引和搜索的基本原理
对于传统数据库,搜索功能都是基于不同的索引方式(B+Tree、倒排索引等)加上精确匹配和排序算法(BM25、TF-IDF)等实现的。
BM25、TF-IDF 本质还是基于文本的精确匹配,这种索引和搜索算法对于关键字的搜索功能非常合适,但对于语义搜索功能就非常弱。
对于向量搜索,通过比较向量之间的距离来判断它们的相似度。
1、相似性测量(Similarity Measurement)
在相似性搜索中,需要计算两个向量之间的距离,然后根据距离来判断它们的相似度。
而如何计算向量在高维空间的距离呢?
有三种常见的向量相似度算法:欧几里德距离、余弦相似度和点积相似度。
(1)欧几里得距离(Euclidean Distance)
欧几里得距离(Eucidean Distance),也称为欧氏距离,是数学中最常见的距离度量方式之一,用于计算两个点在欧几里得空间中的直线距离。
在二维空间中,如果有两个点 P1(x1,y1)和 P2(x2,y2)那么它们之间的欧几里得距离 d 可以通过下面的公式计算:
在三维空间中,如果有两个点 P1(x1,y1,z1)和 P2(x2,y2,z2),那么它们之间的欧几里得距离 d 可以通过下面的公式计算:
对于更高维度的空间,欧几里得距离的计算公式可以推广为:
其中,x1i 和 x2i 分别是点 P1 和 P2在第 i 个维度上的坐标值。
欧几里得距离算法的优点是可以反映向量的绝对距离,适用于需要考虑向量长度的相似性计算。
例如推荐系统中,需要根据用户的历史行为来推荐相似的商品,要考虑用户历史行为的数值,不只是用户历史行为。
(2)余弦相似度(Cosine Similarity)
余弦相似度(Cosine Similarity)是一种常用于度量两个向量在空间中的相似度的方法。
它通过计算两个向量的点积和它们各自的模长来确定这两个向量之间的余弦角,从而反映它们之间的相似程度。
定义
对于两个向量A和B,余弦相似度 cos(0)定义为:
其中:
计算公式:
如果向量A和B分别表示为:
那么:
余弦相似度对向量的长度不敏感,只关注向量的方向,因此适用于高维向量的相似性计算。例如语义搜索和文档分类。
余弦相似度的范围从-1到1,其中1表示完全相同的方向(完全相似),0表示正交(完全不相似),-1表示完全相反的方向(完全不相似)。
(3)点积相似度(Dot product similarity)
点积相似度(Dot Product Similarity),也称为向量内积相似度,是一种衡量两个向量之间相似度的方法。
它通过计算两个向量的点积来评估它们之间的相似性。
定义
对于两个向量 A 和 B,点积相似度定义为:
Dot Product Similarity =A · B
其中,点积 A · B 计算为:
计算公式
如果点积结果为正数,表示两个向量之间存在正相关性,即它们在某种程度上是相似的。
如果点积结果为负数,表示两个向量之间存在负相关性,即它们在某种程度上是相反的。
如果点积结果为零,表示两个向量之间没有线性相关性。
特点
- 无归一化:点积相似度没有进行归一化处理,因此它对向量的长度敏感,这意味着如果两个向量的长度不同,即使它们的方向相同,它们的点积也可能不同。
- 范围:点积的值可以是正的、负的或零,取决于向量之间的相对方向,正点积表示向量之间的角度接近0度,负点积表示向量之间的角度接近180度,而零点积表示向量正交。
与余弦相似度比较
点积相似度与余弦相似度的主要区别在于:
- 点积相似度:对向量的长度敏感,没有归一化。
- 余弦相似度:对向量的长度不敏感,因为已经通过模长进行了归一化。
2、相似性搜索(Similarity Search)
对于向量搜索,通过比较向量之间的距离来判断它们的相似度。
想要在一个海量的数据中找到和某个向量最相似的向量,需要对数据库中的每个向量进行一次比较计算,计算量非常巨大。
高效的搜索算法有很多,其主要思想是通过两种方式提高搜索效率:
减少向量大小——通过降维或减少表示向量值的长度。
缩小搜索范围——可以通过聚类或将向量组织成基于树形、图形结构来实现,并限制搜索范围仅在最接近的族中进行,或者通过最相似的分支进行过滤。
大部分算法共有的核心概念,也就是聚类。
(1)K-Means
K-means算法的核心思想是将数据划分为K个独立的簇(cluster)。
- 使得每个簇内的数据点距离尽可能小。
- 而簇与旗之间的距离尽可能大。
下面是K-means算法的具体步骤:
1.初始化:选择K个数据点作为初始质心(centroid),这些质心可以是随机选择的,也可以是通过其他方法选定的。
2.分配:将每个数据点分配到离它最近的质心所代表的簇中。
3.更新:重新计算每个族的质心,方法是将簇内所有数据点的均值作为新的质心。
4.重复步骤2和3,直到质心不再发生显著变化或达到迭代次数上限。
选择了三个初始质点:
更新完成质点后:
聚类数目(K)的选择
K-Means算法的第一步是确定要将数据划分成多少个簇。
这个选择通常基于领域知识或使用Elbow方法等统计技巧来确定。
K的选择对于聚类结果有着重要的影响。
- 如果K选择过小,可能会导致簇的划分不够细致,无法准确地反映数据的结构;
- 如果K选择过大,可能会导致簇的划分过于细致,会被数据的声影响,导致分类不准确。
距离的度量
K-Means使用欧式距离(Euclidean distance)来度量数据点之间的相似性,但也可以根据具体问题选择其他距离度量方法。
质心
每个簇都有一个质心,它是该簇内所有数据点的均值。质心代表了簇的中心位置。
优化目标
K-Means的优化目标是最小化每个数据点到其所属簇质心的距离之和。
优点
K-means算法具有以下优点:
1.简单易懂:K-means算法的步骡简单,容易理解和实现。
2.计算效率高:K-means算法的时间复杂度相对较低,适用于大规模数据集。
3.可扩展性强:K-means算法可以通过各种改进和优化应用于不同类型的教据和问题。
缺点
K-means算法也存在一些局限性:
1.需要预先指定K值:在实际应用中,选定合适的K值可能需要尝试多种方法。
2.对初始质心敏感:算法的结果可能受到初始质心选择的影响,导致局部最优解。
3.对噪声和离群点敏感:K-means算法容易受到噪声和离群点的影响,可能导致簇划分不准确。
4.对簇形状和大小敏感:K-means算法假设簇是凸的和大小相似的,对于其他形状和大小的簇可能效果不佳。
(2)改进方法
针对K-means算法的局限性,有以下改进方法:
1.选择合适的K值:可以尝试不同的K值,通过轮廓系数(Silhouette Coefficient)、肘部法则(Elbow Method)等方法评估聚类效果,选择最佳的K值。
2.优化初始质心选择:使用K-means++算法改进初始质心选择,降低算法收敛到局部最优解的风险。
3.增量式K-means:对于大规模数据集,可以采用增量式K-means算法进行分布式计算,提高计算效率。
4.引入核函数:将K·means算法扩展为Kernel K-means算法,使用核函数将数据映射到高维空间,处理非线性可分的数据。
肘部法则(Elbow Method)
基本原理是,随着簇数量K的增加,每个簇内的样本数会减少,因此簇内样本与簇中心的平均距离(即簇内误差平方和SSE)会逐渐减小。
当K增加到一定程度后,每增加一个簇,SSE的下降幅度会逐渐减小,形成一个“肘部”拐点。
这个拐点就是最佳的簇数量,因为在此之后增加簇数所带来的分类精度提升将不再显著。
在实际操作中,我们通常会绘制一个曲线图,横轴表示簇的数量K,纵轴表示SSE。
然后,观察曲线的变化趋势,寻找“肘部”位置。
在Python中,可以使用matplotlib库来绘制这个曲线图,并通过观察图形来确定最佳的K值。
K-means++
K-means++ 是一种改进的 K-means 算法,主要针对初始质心选择的问题。
K-means++的优势在于能够选择更好的初始质心,从而提高算法的收敛速度,降低陷入局部最优解的风险。
初始质心选取的基本思路就是,初始的聚类中心之间的相互距离要尽可能的远。
算法描述如下:
1、随机选取一个样本作为第一个聚类中心c1;
2、计算每个样本与当前已有类聚中心最短距离(即与最近一个聚类中心的距离),用 D(x)表示;
这个值越大,表示被选取作为聚类中心的概率较大;
最后,用轮盘法选出下一个聚类中心;
3、重复2,直到选出k个聚类中心。
4、使用选定的初始质心运行K-means 算法。
Kernel K-means
Kernel K-means 是一种基于核方法的K-means 算法,可以处理非线性可分的数据。
核方法通过将数据映射到高维特征空间,使得原本在低维空间中不可分的教据在高维空间中变得线性可分,
Kernel K-means 的主要步骤如下:
1.选择合适的核函数(如 RBF 核、多项式核等)和参数。
2.将数据集映射到高维特征空间。
3.在高维特征空间中执行 K-means 算法。
4.将聚类结果投影回原始数据空间。
Kernel K-means 可以处理复杂的数据结构,但计算复杂度相对较高,可能不适合大规模数据集。在实际应用中,可以根据问题的特点选择合适的K-means 算法变体。
应用场景
K-means算法广泛应用于各个领域,如:
1.图像分割:将图像中的像素聚类为K个簇,可以实现图像分割和简化。
2.文档聚类:将文档按照内容相似度进行聚类,有助于文档分类、信息检索和推荐系统。
3.客户细分:将客户按照购买行为、兴趣爱好等特征进行聚类,有助于企业针对不同群体制定个性化的营销策略。
4.异常检测:通过聚类,可以发现数据中的离群点或异常点,进而进行异常检测或数据清洗。
5.降维:K-means算法可以与主成分分析(PCA)等降维技术结合,实现数据降维和可视化。
3、近似邻近(ANN)
除了暴力搜索能完美的搜索出最相邻,所有的搜索算法只能在速度和质量还有内存上做一个权衡,这些算法也被称为近似最相邻(Approximate Nearest Neighbor)。
(1)Product Quantization(PQ)乘积量化
在大规模数据集中,聚类算法最大的问题在于内存占用太大。
这主要体现在两个方面,
- 首先因为需要保存每个向量的坐标,而每个坐标都是一个 浮点数 ,占用的内存就已经非常大了。
- 除此之外,还需要维护聚类中心和每个向量的聚类中心索引,这也会占用大量的内存。
对于第一个问题,可以通过量化(Quantization)的方式解决,也就是常见的有损压缩。
假设图片检索库有1000万张图片,每张图片提取多个128维的特征向量,把这128维向量分成8个短向量,每个短向量是16维,也就是说检索库总共包含1000万x8这么多个16维的短向量。
如果当做2维矩阵的话就是1000万行x8列,每一列就是1000万个的短向量,每一列就是由每个原始128维向量对应16维子向量组成。
把每一列的子向量都用 k-means 聚类为 256 类,每一个短向是我们都找到他属于一堆短向量的256类中的哪一类。
这样每一行的8个短向量,都会对应于各自列的256类中的一个(一共有8个独立的256类),用0-255表示(也叫码字)。
从存储上来说是 8/16xsizeof(type),type是如 foat32,float16等等,比如原来16个foat16的向量,变成了一个8bt的值,也就是1/32(保存有256个类的中心数据,不过对比1000万个数据来说已经很小啦)。
现在128维向量,由8个8bit的数字组成。
距离计算
查询x的时候,也需要把x先PQ量化
对称距离计算:
直接使用两个压缩向量x,y的索引值所对应的码字q(x),q(y)之间的距离代替之,而q(x),q(y)之间的距离可以离线计算,相当于计算两个子类中心的距离可以把q(x),q(y)之间的距离制作成查找表,只要按照压缩向量的索引值进行对应的查找就可以了,所以速度非常快,
每k=256个类别对应256x256个距离,一共只需要8x256x256个距离。
非对称距离计算:
使用x,q(y)之间的距离代替x,y之间的距离,其中x是查询向量。
虽然y的个数可能有上百万个,但是q(y)的个数只有k=256个,对于每个x,我们只需要在输入x之后先计算一遍x和k个q(y)的距离,
相当于计算 x和每个子类中心的距离。
(2)分层小世界导航(HNSW)
除了聚类以外,也可以通过构建树或者构建图的方式来实现近似最近邻搜索。
这种方法的基本思想是每次将向量加到数据库中的时候,就先找到与它最相邻的向量,然后将它们连接起来,这样就构成了一个图。
当需要搜索的时候,就可以从图中的某个节点开始,不断的进行最相邻搜索和最短路径计算,直到找到最相似的向量。
使用类似跳表算法,如下图要搜索跳表,从最高层开始,沿着具有最长“跳过”的边向右移动。
如果发现当前节点的值大于要搜索的值,表示超过了目标,因此我们会在下一级中向前一个节点。
HNSW 继承了相同的分层格式,最高层具有更长的边缘(用于快速搜索),而较低层具有较短的边缘(用于准确搜索)。
具体来说,可以将图分为多层,每一层都是一个小世界,图中的节点都是相互连接的。
而且每一层的节点都会连接到上一层的节点,当需要搜索的时候,就可以从最上层开始,因为最上层的节点之间距离很长,可以减少搜索的时间,然后再逐层向下搜索,又因为最下层相似节点之间相互关联,所以可以保证搜索的质量,能够找到最相似的向量。
HNSW 算法是一种经典的空间换时间的算法,它的搜索质量和搜索速度都比较高,但是它的内存开销也比较大,因为不仅需要将所有的向量都存储在内存中,还需要维护一个图的结构,也同样需要存储。所以这类算法需要根据实际的场景来选择。
(3)局部敏感哈希(LSH)
局部敏感哈希(Locality Sensitive Hashing,LSH)也是一种使用近似最近邻搜索的索引技术。它的特点是快速,同时仍然提供一个近似、非穷举的结果。
LSH 使用一组哈希函教将相似向量映射到“桶”中,从而使相似向量具有相同的哈希值,这样,就可以通过比较哈希值来判断向量之间的相似度。
传统hash算法
通常设计的哈希算法都是力求减少哈希碰撞的次数,因为哈希函数的搜索时间复杂度是 O(1)。
如果存在哈希碰撞,即两个不同的关键字被映射到同一个桶中,那么就需要使用链表等教据结构来解决冲突。
搜索的时间复杂度通常是 O(n),其中n是链表的长度。为了提高哈希函数搜索效率,会让哈希函数碰撞概率尽可能小。
局部敏感哈希 LSH
但是在向量搜索中,我们的目的是为了找到相似的向量,可以专门设计一种哈希函数,使得哈希碰撞的概率尽可能高,并且位置越近或者越相似的向量越容易碰撞,这样相似的向量就会被映射到同一个桶中。
在搜索特定向量时,为了找到给定查询向量的最近邻居,使用相同的哈希函教将类似向量“分桶“到哈希表中。
查询向量被散列到特定表中,然后与该表中的其他向量进行比较以找到最接近的匹配项。
这种方法比搜索整个数据集要快得多,因为每个哈希表桶中的向是远少于整个空间中的向量数。
其实就是将高维数据转化为低维数据,同时还能在一定程度上保持原始数据的相似性。
但 LSH 是不确定的,是概率性的,有可能将两个原本很相似的数据映射成两个不同的 hash 值,或者原本不相似的数据映射成同一hash值。
这是高维数据降维过程中所不能避免的(因为降维势必会造成某种程度上数据的失真),可以设计LSH 让参数控制出现这种错误的概率。
Milvus 的存储设计
Milvus是一个高性能的向量 数据库 ,专为处理大规模向量数据而设计。它采用了一种混合存储架构,结合了内存存储和磁盘存储的优点,以提高数据处理的效率和灵活性。
Milvus 内存存储
Milvus利用内存存储进行高效的数据处理和实时查询。当数据被插入时,首先存储在内存中。内存存储的主要优点是速度快,可以实现低延迟的实时查询。
Milvus 磁盘存储
为了持久化数据,Milvus会定期将内存中的数据刷写到磁盘上。
磁盘存储的主要优点是容量大,可以存储海量的数据。通过这种方式,Milvus既能提供高效的查询性能,又能保证数据的持久性。
Milvus 数据存储架构的设计思想
Milvus的数据存储架构设计思想是结合内存和磁盘的优点,达到性能和持久性之间的平衡。
这种设计有以下几个优点:
(1) 高性能:通过内存存储,Milvus可以实现低延迟的实时查询,满足高性能需求。
(2) 大容量:通过磁盘存储,Milvus可以存储海量数据,满足大规模数据处理需求。
(3) 数据安全:通过定期将内存数据刷写到磁盘,Milvus保证了数据的持久性和安全性。
(4) 灵活性:混合存储架构使得Milvus在处理不同规模和类型的数据时具有更大的灵活性。
数据存储架构设计思想高性能大容量数据安全灵活性
Milvus 数据存储的架构和设计思想
Milvus的数据存储架构结合了内存和磁盘的优点,既能提供高效的查询性能,又能保证数据的持久性。
以下是数据存储架构的详细设计思想和优点:
1. 混合存储架构
Milvus采用混合存储架构,将数据存储在内存和磁盘中。
内存存储用于高效的实时查询,而磁盘存储用于持久化数据。
通过这种方式,Milvus既能提供高性能的查询,又能保证数据的持久性。
2. 分层存储
数据分层存储的设计使得Milvus能够根据数据的重要性和访问频率,动态调整存储策略。
热数据存储在内存中,冷数据存储在磁盘上。
这种分层存储的设计提高了数据的访问效率。
3. 高并发支持
Milvus的数据存储架构支持高并发访问,能够处理大量并发查询请求。
通过优化内存和磁盘的读写操作,Milvus实现了高并发访问下的高效查询。
4. 自动化运维
Milvus的数据存储架构支持自动化运维,
能够自动进行数据备份、故障恢复等操作。
通过自动化运维,Milvus保证了数据的安全性和可用性。
数据存储架构设计思想混合存储架构分层存储高并发支持自动化运维内存存储磁盘存储热数据冷数据
优点
(1) 高性能:通过内存存储,Milvus能够提供低延迟的实时查询。
(2) 大容量:通过磁盘存储,Milvus能够存储海量数据。
(3) 数据安全:通过定期将内存数据刷写到磁盘,Milvus保证了数据的持久性和安全性。
(4) 灵活性:混合存储架构使得Milvus在处理不同规模和类型的数据时具有更大的灵活性。
(5) 高并发:通过优化内存和磁盘的读写操作,Milvus实现了高并发访问下的高效查询。
(6) 自动化:自动化运维保证了数据的安全性和可用性,减少了运维成本。
数据存储架构优点高性能大容量数据安全灵活性高并发自动化
Milvus的 索引类型 与适用场景
索引是提高检索效率的重要手段。
Milvus提供了多种索引类型,以适应不同的应用场景。
以下是Milvus支持的主要索引类型及其适用场景:
Milvus索引类型
- IVF 倒排文件
- HNSW
- ANNOY
- FLAT
- DISKANN
[table]索引类型适用场景特点典型参数IVF大规模数据,平衡速度与精度聚类+倒排列表,查询可控nlist=128HNSW高维数据,高精度要求分层图结构,精度优先M=16, ef=200ANNOY中等规模,内存敏感场景随机森林,低内存占用n_trees=10FLAT小数据,100%召回率暴力搜索,精确匹配无需参数DISKANN超大规模数据,磁盘优化磁盘驻留索引,减少内存依赖search_cache_size=2GB1、IVF(Inverted File) 倒排文件
IVF(Inverted File)是一种基于倒排文件的索引结构,通过对向量进行聚类,创建倒排列表。
每个倒排列表存储一组相似的向量,从而加速近似最近邻搜索。
IVF 倒排文件 适用场景
IVF适用于大规模数据集,特别是在需要快速近似搜索的场景中。
IVF 在查询速度和存储空间之间取得了良好的平衡。
IVF 倒排文件 架构
IVF索引的构建过程包括以下几个步骤:
(1) 聚类:使用K-means算法将数据集划分为若干个簇。
(2) 创建倒排列表:每个簇对应一个倒排列表,存储属于该簇的向量。
(3) 搜索:在查询时,首先找到与查询向量最近的簇,然后在该簇的倒排列表中进行精确搜索。
Java代码示例
- kubectl get pods -n milvus -l app=milvus-proxy
复制代码 python代码示例- from pymilvus import connections, Collection
- connections.connect(host="<K8S_NODE_IP>", port=30001) # 使用 NodePort 地址
- print(connections.list_collections()) # 应返回空列表(新集群)
复制代码 2、HNSW(Hierarchical Navigable Small World)
HNSW是一种基于图的索引结构,通过构建小世界图,实现高效的向量检索。
HNSW的核心思想是利用小世界特性,进行快速的近似搜索。
HNSW 适用场景
HNSW适用于高维向量数据集,尤其是在需要高精度搜索的场景中。它在查询速度和精度之间取得了良好的平衡。
HNSW 架构
HNSW索引的构建过程包括以下几个步骤:
(1) 构建层次结构:将向量按照不同层次进行组织,较高层次的节点连接较多,较低层次的节点连接较少。
(2) 构建小世界图:在每个层次构建小世界图,节点之间的连接遵循小世界特性。
(3) 搜索:在查询时,从最高层开始,通过小世界图的导航,逐层向下找到最相似的向量。
Java代码示例
- - name: rootcoord
- image: milvusdb/milvus:v2.3.3
- command: ["milvus", "run", "rootcoord"]
复制代码 python代码示例- [/code][size=3]3、 ANNOY(Approximate Nearest Neighbors Oh Yeah)[/size]
- ANNOY(Approximate Nearest Neighbors Oh Yeah)是一种基于随机树的索引结构,通过构建多棵随机树,实现近似最近邻搜索。
- ANNOY的核心思想是利用随机树结构,进行快速的近似搜索。
- [size=2]ANNOY适用场景[/size]
- ANNOY适用于中等规模的数据集,特别是在内存受限的场景中。它在查询速度和内存消耗之间取得了良好的平衡。
- [size=2]ANNOY架构[/size]
- ANNOY索引的构建过程包括以下几个步骤:
- [b](1) 构建随机树:通过随机选择数据点和分裂点,构建多棵随机树。[/b]
- [b](2) 搜索:在查询时,利用多棵随机树进行搜索,并合并结果,找到近似最近邻。[/b]
- [size=2]Java代码示例[/size]
- [code]##### (3) 分离 Milvus 组件角色**
- - 专用 Root Coordinator、Data Node、Query Node,避免资源竞争。
- - 示例配置(YAML 片段):
复制代码 python代码示例- #### **5. 总结**
- - **etcd**:Milvus 的“大脑”,管理元数据和集群状态。
- - **MinIO**:Milvus 的“硬盘”,持久化存储向量和索引文件。
- - **协作逻辑**:
- 通过 etcd 维护一致性,通过 MinIO 实现数据持久化,Milvus 各组件基于两者协作完成向量数据的存储、索引和查询。
- 三者缺一不可,共同保障系统的可靠性、扩展性和高性能。
- # Python Milvus连接管理
- ## 管理Milvus连接
- 本主题介绍了如何连接和断开Milvus服务器。
- 在进行任何操作之前,请确保连接到了Milvus服务器。
- Milvus支持两个端口,端口`19530`和端口`9091`:
- - 端口`19530`用于gRPC。当使用不同的Milvus SDK连接到Milvus服务器时,它是默认端口。
- - 端口`9091`用于RESTful API。当使用HTTP客户端连接到Milvus服务器时,使用该端口。
- 下面的示例连接到主机为`localhost`,端口为`19530`或`9091`的Milvus服务器,并断开连接。如果连接被拒绝,请尝试解除相应端口的阻止。
- ## 连接到Milvus服务器
- 构建一个Milvus连接。在进行任何操作之前,请确保连接到Milvus服务器。
复制代码 4、 FLAT(Brute-force)
FLAT(Brute-force)是一种基于暴力搜索的索引结构,通过遍历所有向量进行精确搜索。
FLAT的核心思想是利用线性扫描,进行精确的最近邻搜索。
FLAT 适用场景
FLAT适用于小规模的数据集,特别是在需要高精度搜索的场景中。它在查询精度和计算开销之间取得了良好的平衡。
FLAT 架构
FLAT索引的构建过程非常简单,主要包括以下步骤:
(1) 存储向量:将所有向量存储在一个数组中。
(2) 搜索:在查询时,遍历所有向量,计算距离,找到最近邻。
FLAT原理存储向量数组存储搜索遍历所有向量计算距离找到最近邻
Java代码示例
- | 参数 | 描述 |
- | :--------- | :------------------------- |
- | `alias` | 要构建的Milvus连接的别名。 |
- | `user` | Milvus服务器的用户名。 |
- | `password` | Milvus服务器用户名的密码。 |
- | `host` | Milvus服务器的IP地址。 |
- | `port` | Milvus服务器的端口。 |
- 默认安装Milvus没有设置密码
- ### 返回值
- 通过传递的参数创建的Milvus连接。
- ### 异常
- - **NotImplementedError**:如果连接参数中的处理程序不是GRPC。
- - **ParamError**:如果连接参数中的池不受支持。
- - **异常**:如果参数中指定的服务器没有准备好,我们无法连接到服务器。
- # Python Milvus数据库管理
- ------
- ## 管理数据库
- 与传统的数据库引擎类似,您也可以在Milvus中创建数据库,并为特定用户分配权限来管理它们。然后这些用户有权管理数据库中的集合。Milvus集群支持最多64个数据库。
- ## 创建数据库
- 要创建数据库,您需要首先连接到Milvus集群,并准备一个名称:
复制代码 5、DISKANN(Disk-based Approximate Nearest Neighbors)
DISKANN(Disk-based Approximate Nearest Neighbors)是一种基于磁盘的近似最近邻搜索索引,通过将数据存储在磁盘上,实现大规模数据集的高效检索。
DISKANN的核心思想是利用磁盘存储,进行快速的近似搜索。
DISKANN适用场景
DISKANN适用于超大规模的数据集,特别是在内存受限但需要高效检索的场景中。它在存储容量和查询速度之间取得了良好的平衡。
DISKANN架构
DISKANN索引的构建过程包括以下几个步骤:
(1) 构建索引:将数据分块并存储在磁盘上,创建索引文件。
(2) 加载索引:在查询时,从磁盘加载索引文件。
(3) 搜索:利用磁盘上的索引,进行快速的近似搜索。
DISKANN原理构建索引数据分块存储在磁盘加载索引从磁盘加载搜索利用索引快速搜索
Java代码示例
- ## 使用数据库
- Milvus集群附带了一个默认数据库,名为”default”。除非另有指定,否则集合将在默认数据库中创建。
- 要更改默认数据库,请按照以下步骤操作:
复制代码 python代码示例- 您还可以在连接到Milvus集群时设置要使用的数据库,示例如下:
复制代码 向量数据库Milvus的索引选择和优化策略:
索引选择策略
维度一:根据数据规模选择:
- 小规模数据集:当数据集规模较小时,如千级向量数据,FLAT索引是一个不错的选择。它能够提供精确的搜索结果,并且查询时间相对较短。
- 中等规模数据集:对于中等规模的数据集,如十万级向量数据,ANNOY索引较为合适。它能在查询速度和内存消耗之间取得良好平衡,适合内存受限的场景。
- 大规模数据集:在处理大规模数据集时,IVF和DISKANN是常用的索引类型。IVF通过聚类和倒排列表加速查询,适用于数据量较大且对查询速度有一定要求的场景。DISKANN则通过磁盘存储实现大规模数据集的高效检索,适合内存资源有限但需要处理大规模数据的情况。
- 高维向量数据集:对于高维稠密向量数据,如文本嵌入、图像特征等,HNSW索引表现出色。它基于小世界网络的思想,能维持较高的查询精度,同时提供较快的查询速度。
维度二:根据查询精度要求选择:
- 高精度要求:如果应用场景对查询精度要求极高,如一些需要精确匹配的检索任务,FLAT索引是首选,因为它能保证100%的召回率,提供精确的搜索结果。此外,IVF_FLAT和HNSW等索引类型也可以通过调整参数来提高查询精度。
- 适度精度要求:在对查询精度要求不是绝对严格的情况下,可以选择一些在精度和速度之间取得平衡的索引类型,如IVF、ANNOY等。这些索引能够在一定程度上满足查询精度的需求,同时提供较快的查询速度。
维度三:根据存储和计算资源选择:
- 存储资源充足:当存储资源较为充足时,可以选择一些存储开销相对较大的索引类型,如HNSW、IVF_FLAT等。这些索引类型通常能够提供更好的查询性能。
- 存储资源有限:如果存储资源受限,PQ索引是一个较好的选择。它通过量化的方式减少存储和计算开销,适用于内存和存储资源有限的场景。
索引优化策略
一:调整索引参数:
- IVF相关参数:对于IVF类索引,nlist参数表示簇的数量,一般建议nlist=4×sqrt(N),其中N为数据量。例如,对于100万数据量,nlist可设置为2000左右。nprobe参数表示查询时搜索的簇的数量,nprobe越大,召回率越高,但查询性能可能下降。通常可以从nprobe=16开始尝试,根据实际情况进行调整。
- HNSW相关参数:M参数控制每个节点的最大连接数,M越大,查询精度越高,但内存消耗也越大,一般建议设置在8-32之间。efConstruction参数用于控制图的构建精度,值越大,构建时会搜索更多的候选节点,精度越高,但构建时间也越长。efSearch参数在查询时使用,值越大,查询精度越高,但查询时间也会相应增加。
- PQ相关参数:m参数表示将每个向量拆分成的子向量数量,m越大,精度越高,但存储需求和计算开销也越大。nbits参数表示每段量化器占用的bit数目,默认为8,一般不建议调整。
二:结合索引类型:在某些场景下,可以考虑结合多种索引类型来提高查询效率和精度。例如,将IVF和HNSW结合,先通过IVF缩小搜索空间,再在每个簇内部使用HNSW进行精确的相似度计算,适用于数据量大且需要高精度的场景。
三:定期更新索引:随着数据的不断插入和更新,索引的性能可能会受到影响。因此,需要定期对索引进行更新和重建,以保证查询性能。例如,当数据量增加到一定程度或查询性能明显下降时,可以考虑重建索引。
四:监控和评估索引性能:在实际应用中,需要持续监控和评估索引的性能,包括查询延迟、吞吐量、召回率等指标。通过分析这些指标,可以及时发现索引性能问题,并采取相应的优化措施。
五:硬件资源优化:合理配置和优化硬件资源也对索引性能有重要影响。例如,增加内存容量、使用高速存储设备、优化网络带宽等,都可以提高Milvus的查询性能和索引构建速度
Milvus 的默认索引机制
Milvus 不主动创建任何索引,当未显式指定索引时,默认使用 FLAT(暴力搜索)模式进行向量检索。
FLAT(暴力搜索)模式 模式特点:
- 无需预处理:数据插入后直接可查询
- 100%召回率:保证搜索结果的绝对准确性
- 线性复杂度:查询时间与数据量成正比
行为表现
- 适用场景:数据量 < 100万的低频查询验证场景
- 性能特征:CPU单核查询速度约 200 QPS(128维,100万数据)
- 存储要求:原始向量数据量 × 内存系数(如100万128维向量占用约500MB内存)
IVF 是常用的索引类型
IVF(Inverted File)是一种基于倒排文件的索引结构,通过对向量进行聚类,创建倒排列表。
每个倒排列表存储一组相似的向量,从而加速近似最近邻搜索。
IVF 倒排文件 适用场景
IVF适用于大规模数据集,特别是在需要快速近似搜索的场景中。
IVF 在查询速度和存储空间之间取得了良好的平衡。
创建IVF索引 的 python代码示例- from pymilvus import connections, Collection
- connections.connect(host="<K8S_NODE_IP>", port=30001) # 使用 NodePort 地址
- print(connections.list_collections()) # 应返回空列表(新集群)
复制代码 查询 IVF索引 的 python代码示例- ## 删除数据库
- 要删除数据库,您必须先删除其所有集合。否则,删除将失败。
复制代码 遇到问题,找老架构师取经
借助此文,尼恩给解密了一个高薪的 秘诀,大家可以 放手一试。保证 屡试不爽,涨薪 100%-200%。
后面,尼恩java面试宝典回录成视频, 给大家打造一套进大厂的塔尖视频。
通过这个问题的深度回答,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,里边有大量的大厂真题、面试难题、架构难题。
很多小伙伴刷完后, 吊打面试官, 大厂横着走。
在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。
另外,如果没有面试机会,可以找尼恩来改简历、做帮扶。
遇到职业难题,找老架构取经, 可以省去太多的折腾,省去太多的弯路。
尼恩指导了大量的小伙伴上岸,前段时间,刚指导一个40岁+被裁小伙伴,拿到了一个年薪100W的offer。
狠狠卷,实现 “offer自由” 很容易的, 前段时间一个武汉的跟着尼恩卷了2年的小伙伴, 在极度严寒/痛苦被裁的环境下, offer拿到手软, 实现真正的 “offer自由” 。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |