向量搜索之 k-means 算法(annoy向量检索)
itomcoil 2025-07-24 18:47 5 浏览
一直好奇向量数据库的索引是如何实现的,我们可以推断向量搜索的简单实现:把数据存入向量数据库时,会计算每个分段文档的向量(文档向量),然后把分段文档和文档向量同时存入向量数据库。从向量数据库中搜索文档时,会把待搜索问题转为向量(问题向量),然后计算问题向量与所有文档向量的距离,数据库会返回距离最短的一个或多个文档。
上面计算方式,返回的文档与问题最相关,但文档多时,耗费的计算资源同样多,有没有更好的方法?最容易想到的方法是把存入数据库中的文档向量先聚类为 K 个簇,从向量数据库中搜索文档时,先找到最相近的簇,再和簇内的每个文档向量比较找到与问题向量距离最短的一个或多个文档。这样需要的计算量就会少很多。
K-means算法简介
怎么把文档向量聚类为 K 个簇?可以使用K-means 算法。
- 初始化:随机选择K个数据点作为初始簇中心 。
- 分配样本:计算每个数据点到各簇中心的距离(通常用欧氏距离),将其分配到最近的簇 。
- 更新簇中心:重新计算每个簇的均值作为新中心 。
- 迭代:重复分配和更新步骤,直到中心不再变化或达到最大迭代次数
K-means 算法 python 实现
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
# 生成模拟数据,300个数据点,4个簇,每个簇的标准差为0.6,随机种子为0
X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)
# K-means算法实现
def k_means(X, K, max_iters=100):
np.random.seed(42)
# 1. 随机初始化:从数据集中随机选择K个点作为初始簇中心
# np.random.choice 是随机抽样函数
# X.shape[0] 表示模拟数据个数;
# replace=False 表示无放回抽样,即每个数据只能被选中一次,不允许重复
centroids = X[np.random.choice(X.shape[0], K, replace=False)]
original_centroids = centroids.copy()
for iteration in range(max_iters):
# 2. 分配数据点:计算每个数据点到各个簇中心的距离
# X[:, np.newaxis] 表示为 X 添加一个新维度,使其形状变为 (n_samples, 1, n_features)。
# 目的是通过广播机制与 centroids 的维度 (K, n_features) 对齐,便于后续逐元素计算
# X[:, np.newaxis] - centroids 广播机制会将 X 扩展为 (n_samples, K, n_features),
# centroids 扩展为 (n_samples, K, n_features)(实际仅逻辑扩展,不占用额外内存)
distances = np.linalg.norm(X[:, np.newaxis] - centroids, axis=2)
# 3. 将每个数据点分配到最近的簇
labels = np.argmin(distances, axis=1)
# 4. 更新簇中心:重新计算每个簇的中心点,即簇内所有点的均值
# labels == k 生成布尔掩码(Boolean Mask),筛选出 labels 数组中标签等于 k 的所有样本索引
# X[labels == k] 根据布尔掩码从数据矩阵 X 中提取属于第 k 个簇的所有样本
# .mean(axis=0) 沿列方向(axis=0)计算均值,得到第 k 个簇的中心坐标
new_centroids = np.array([X[labels == k].mean(axis=0) for k in range(K)])
# 5. 迭代优化:检查簇中心是否变化
if np.all(centroids == new_centroids):
print(f"算法在第{iteration + 1}次迭代后收敛")
break
centroids = new_centroids
return original_centroids, centroids, labels
# 使用K-means算法进行聚类
K = 4 # 指定簇的数量
original_centroids, centroids, labels = k_means(X, K)
# 结果可视化
plt.figure(figsize=(8, 6))
# 每个簇的数据使用不同颜色展示
plt.scatter(X[:, 0], X[:, 1], c=labels, s=50, cmap='viridis', edgecolor='k')
# 最终得到的簇心用红色展示
plt.scatter(centroids[:, 0], centroids[:, 1], c='red', s=200, alpha=0.5, marker='X')
# 最初得到的簇心用蓝色展示
plt.scatter(origcentroids[:, 0], origcentroids[:, 1], c='blue', s=200, alpha=0.5, marker='X')
plt.title("K-means Clustering")
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
# 保存到本地图片
plt.savefig("k_means_clustering.png")
上面实现中 distances = np.linalg.norm(X[:, np.newaxis] - centroids, axis=2) 等价于下面代码,计算每个数据点到各个簇中心的距离。
distances = np.zeros((X.shape[0], centroids.shape[0]))
for i in range(X.shape[0]):
for j in range(centroids.shape[0]):
distances[i, j] = np.sqrt(np.sum((X[i] - centroids[j])**2))
展示最终得到的簇中心与各个簇如下图所示
同时展示 K-means 算法最初随机选择的簇中心,使用蓝色叉展示如下,与最终的簇中心位置相隔较远,可见算法帮我们动态调整了簇中心。
向量数据库进行实际的向量搜索时,并不是简单通过 K-meams 算法创建索引,本文是从一个算法小白角度推导向量搜索如何实现,接下来会逐步介绍实际使用的向量搜索算法。
相关推荐
- Python高效数据处理——从基础方法到性能优化
-
数据处理是数据分析的核心环节,高效的数据处理方法能显著提升代码性能。本文将深入介绍Pandas中的各种数据处理技术,并分析它们的性能特点。使用apply方法应用自定义函数apply是Pandas中最灵...
- 正态分布-置信区间计算(正态90%置信区间)
-
统计学有两大主要分支,分别是描述性统计学和推断统计学。描述性统计学用于描述和概括数据的特征以及绘制各类统计图表。总体数据,往往因为数据量太大而难以被获取,所以就有了通过较小的样本数据推测总体特性的推断...
- 一篇文章搞定人工智能之深度学习创建训练数据集的方法
-
基础数据准备训练所需要的数据集合都存储在数据库中,还有部分文本文件首先对数据进行分类结构化存储[因为涉及到的是多分类问题]整理并存储原始数据集使用numpy将所有需要数据读取出来splitlines(...
- 向量搜索之 k-means 算法(annoy向量检索)
-
一直好奇向量数据库的索引是如何实现的,我们可以推断向量搜索的简单实现:把数据存入向量数据库时,会计算每个分段文档的向量(文档向量),然后把分段文档和文档向量同时存入向量数据库。从向量数据库中搜索文档时...
- 融合贝叶斯生存模型与Transformer注意力的客户重参与策略优化
-
本文提出了一个集成三种核心技术的下一代智能优惠券分发系统:基于贝叶斯生存模型的重购概率预测、采用注意力机制的Transformer利润预测模型,以及用于策略持续优化的Dyna-Q强化学习代理。该系统构...
- 用Deepseek编写代码计算今天大乐透开奖号码
-
以下是一个基于Python的示例代码,用于分析大乐透历史数据并生成可能的号码组合。请务必注意:这仅是统计学模拟,无法真正预测开奖结果,所有结果均为随机性参考。代码实现步骤1.数据准备(模拟数据)假设...
- 拆解特斯拉L2家用充电桩:技术细节太多了
-
本文是对第三代特斯拉家用充电桩(L2级)的拆解分析报告。深入探究该充电桩的内部结构、设计特点、性能参数等内容。产品概述设备为第三代特斯拉家用充电桩,属于Level2充电器,是特斯拉推出的家用充电设备...
- 《光环5》2月更新“战锤风暴”正式推送“枪林弹雨”模式即将到来
-
今天(2月25日)微软和343工作室正式向Xboxone玩家推送了《光环5》的2月更新补丁“战锤风暴HammerStorm”。本次更新包括了1张全新Arena竞技场地图Torque;3个全新游戏模式...
- Spring Boot(十一)Redis集成从Docker安装到分布式Session共享
-
一、简介Redis是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API,Redis也是技术领域使用最为广泛的存储中间件,它是「...
- Mac 基于HTTP方式访问下载共享文件,配置共享服务器
-
方法一:使用Python的SimpleHTTPServer进行局域网文件共享Mac自带Python,所以不需要安装其他软件,一条命令即可1):进入需要共享的文件夹,如Public文件夹cd/Us...
- 移动端性能专项测试之 CPU(移动端cpu天梯图2020百度贴吧)
-
指标背景很多场景下我们去使用App,可能会碰到手机会出现发热发烫的现象。这是因为CPU使用率过高、CPU过于繁忙,会使得整个系统无法响应用户,整体性能降低,用户体验变得相当差,也容易引起AN...
- 如何三天学会Phyton?这篇文章教你快速编程入门
-
Phyton作为一门常用的语言在很多领域都有很应用,很多人都想学习这门语言,那么我们就开始从头学习这门语言吧!首先你需要在官网下载你的Phyton的编程工具,也就是下载你的解释器!登录Phyton官网...
- 学习Python第一天 ---Hello World
-
引言人生苦短,请用Python(3.+)越来越多的情况下使用Python语言进行"代码粘合"和"数据分析"变得非常方便,而且Python在"爬虫"...
- mysql的MVCC多版本并发控制机制(mysql并发情况下怎么解决)
-
认识MVCCMVCC是英文Multi-VersionConcurrencyControl多版本并发控制的首字母简拼。在上文MYSQL事务隔离级别中,我们已经知道,在可重复读的级别下,不管其他事...
- 爆炸,MySQL9.0大版本发布,我严重怀疑,它是不...
-
MySQL在本月发布了9.0大版本,作为MySQL的忠实粉丝,简单说下这次大版本更新。1.企业版,支持JS存储程序(JavaScriptstoredprograms)了。例如,可以像这样定一个函...
- 一周热门
- 最近发表
- 标签列表
-
- ps图案在哪里 (33)
- super().__init__ (33)
- python 获取日期 (34)
- 0xa (36)
- super().__init__()详解 (33)
- python安装包在哪里找 (33)
- linux查看python版本信息 (35)
- python怎么改成中文 (35)
- php文件怎么在浏览器运行 (33)
- eval在python中的意思 (33)
- python安装opencv库 (35)
- python div (34)
- sticky css (33)
- python中random.randint()函数 (34)
- python去掉字符串中的指定字符 (33)
- python入门经典100题 (34)
- anaconda安装路径 (34)
- yield和return的区别 (33)
- 1到10的阶乘之和是多少 (35)
- python安装sklearn库 (33)
- dom和bom区别 (33)
- js 替换指定位置的字符 (33)
- python判断元素是否存在 (33)
- sorted key (33)
- shutil.copy() (33)