elkan K-Means算法

news/2025/2/26 12:20:22/

简介

在计算向量相似度时,常用 近似最近邻(ANN, Approximate Nearest Neighbor)算法 来加速查询向量的搜索。其中,较为知名的 ANN 算法包括 HNSW、Ivfflat、Ivfpq 和 Ivfsq。在 IVF(倒排索引,Inverted File Index) 类型的算法中,Elkan K-Means 算法是较为经典的方法之一,并被广泛用于向量聚类和索引构建。

在 PostgreSQL 中,pgvector 插件提供了对向量数据的索引与搜索支持,而 Elkan K-Means 算法正是其中用于优化 IVF 聚类过程的关键技术。接下来,我们将深入解析 Elkan K-Means 算法的具体执行流程。

算法详情

C语言编写的完整代码过程(其中的宏和其他的内容暂不解释,主要讲解代码逻辑):

static void
ElkanKmeans(Relation index, VectorArray samples, VectorArray centers, const IvfTypeInfo * typeInfo)
{FmgrInfo   *procinfo;FmgrInfo   *normprocinfo;Oid			collation;int			dimensions = centers->dim;int			numCenters = centers->maxlen;int			numSamples = samples->length;VectorArray newCenters;float	   *agg;int		   *centerCounts;int		   *closestCenters;float	   *lowerBound;float	   *upperBound;float	   *s;float	   *halfcdist;float	   *newcdist;/* Calculate allocation sizes */Size		samplesSize = VECTOR_ARRAY_SIZE(samples->maxlen, samples->itemsize);Size		centersSize = VECTOR_ARRAY_SIZE(centers->maxlen, centers->itemsize);Size		newCentersSize = VECTOR_ARRAY_SIZE(numCenters, centers->itemsize);Size		aggSize = sizeof(float) * (int64) numCenters * dimensions;Size		centerCountsSize = sizeof(int) * numCenters;Size		closestCentersSize = sizeof(int) * numSamples;Size		lowerBoundSize = sizeof(float) * numSamples * numCenters;Size		upperBoundSize = sizeof(float) * numSamples;Size		sSize = sizeof(float) * numCenters;Size		halfcdistSize = sizeof(float) * numCenters * numCenters;Size		newcdistSize = sizeof(float) * numCenters;/* Calculate total size */Size		totalSize = samplesSize + centersSize + newCentersSize + aggSize + centerCountsSize + closestCentersSize + lowerBoundSize + upperBoundSize + sSize + halfcdistSize + newcdistSize;/* Check memory requirements *//* Add one to error message to ceil */if (totalSize > (Size) maintenance_work_mem * 1024L)ereport(ERROR,(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),errmsg("memory required is %zu MB, maintenance_work_mem is %d MB",totalSize / (1024 * 1024) + 1, maintenance_work_mem / 1024)));/* Ensure indexing does not overflow */if (numCenters * numCenters > INT_MAX)elog(ERROR, "Indexing overflow detected. Please report a bug.");/* Set support functions */procinfo = index_getprocinfo(index, 1, IVF_KMEANS_DISTANCE_PROC);normprocinfo = IvfOptionalProcInfo(index, IVF_KMEANS_NORM_PROC);collation = index->rd_indcollation[0];/* Allocate space *//* Use float instead of double to save memory */agg = palloc(aggSize);centerCounts = palloc(centerCountsSize);closestCenters = palloc(closestCentersSize);lowerBound = palloc_extended(lowerBoundSize, MCXT_ALLOC_HUGE);upperBound = palloc(upperBoundSize);s = palloc(sSize);halfcdist = palloc_extended(halfcdistSize, MCXT_ALLOC_HUGE);newcdist = palloc(newcdistSize);/* Initialize new centers */newCenters = VectorArrayInit(numCenters, dimensions, centers->itemsize);newCenters->length = numCenters;#ifdef IVFFLAT_MEMORYShowMemoryUsage(MemoryContextGetParent(CurrentMemoryContext));
#elif defined IVFPQ_MEMORY	ShowMemoryUsage(MemoryContextGetParent(CurrentMemoryContext));
#endif/* Pick initial centers */InitCenters(index, samples, centers, lowerBound);/* Assign each x to its closest initial center c(x) = argmin d(x,c) */for (int64 j = 0; j < numSamples; j++){float		minDistance = FLT_MAX;int			closestCenter = 0;/* Find closest center */for (int64 k = 0; k < numCenters; k++){/* TODO Use Lemma 1 in k-means++ initialization */float		distance = lowerBound[j * numCenters + k];if (distance < minDistance){minDistance = distance;closestCenter = k;}}upperBound[j] = minDistance;closestCenters[j] = closestCenter;}/* Give 500 iterations to converge */for (int iteration = 0; iteration < 500; iteration++){int			changes = 0;bool		rjreset;/* Can take a while, so ensure we can interrupt */CHECK_FOR_INTERRUPTS();/* Step 1: For all centers, compute distance */for (int64 j = 0; j < numCenters; j++){Datum		vec = PointerGetDatum(VectorArrayGet(centers, j));for (int64 k = j + 1; k < numCenters; k++){float		distance = 0.5 * DatumGetFloat8(FunctionCall2Coll(procinfo, collation, vec, PointerGetDatum(VectorArrayGet(centers, k))));halfcdist[j * numCenters + k] = distance;halfcdist[k * numCenters + j] = distance;}}/* For all centers c, compute s(c) */for (int64 j = 0; j < numCenters; j++){float		minDistance = FLT_MAX;for (int64 k = 0; k < numCenters; k++){

http://www.ppmy.cn/news/1575002.html

相关文章

AI知识架构之数据采集

数据采集 数据格式: 结构化数据:以固定格式和结构存储,如数据库中的表以及 Excel 表格,易于查询和分析。半结构化数据:有一定结构但不如结构化数据严格,XML 常用于数据交换,JSON 在 Web 应用中广泛用于数据传输和存储。非结构化数据:无预定义结构,文本、图像、音频和视…

大语言模型学习路径与开源模型推荐

互联网各领域资料分享专区(不定期更新): Sheet 正文 一、入门级开源模型推荐 1. GPT-2(小参数版) 特点:由OpenAI推出,117M参数的版本对硬件要求较低,适合新手理解生成式模型的基本原理(如自回归生成、注意力机制)。学习方向:可尝试文本生成、对话模拟等任务,结合论…

git 命令 设置别名

在Git中&#xff0c;您可以通过以下命令查看所有的alias&#xff08;别名&#xff09;&#xff1a; git config --get-regexp alias 这个命令会列出所有配置的alias&#xff0c;例如&#xff1a; alias.st.status alias.co.checkout alias.br.branch ... 如果您想查看某个特定a…

“国补”带火手机换新,出售旧手机应如何保护个人信息安全

在“国补”政策的推动下,手机换新热潮正席卷而来。“国补”以其诱人的补贴力度,成功激发了消费者更换手机的热情。无论是渴望体验最新技术的科技爱好者,还是对旧手机性能不满的普通用户,都纷纷投身到这场手机换新的浪潮之中。 随着大量消费者参与手机换新,二手手机市场迎来…

vue3学习3-route

创建路由器&#xff1a; 应用路由器&#xff1a; 路由展示区RouterView 和 路由跳转RouterLink&#xff1a; 路由组件&#xff08;在路由配置文件中配置的&#xff09;一般放到pages/views文件夹下 路由组件切换的时候执行的是 挂载/卸载操作 onMounted / onUnmouted 路由器两…

国产单片机开发汽车气压表胎压计解决方案

一、技术原理 &#xff08;一&#xff09;压力传感技术 压电式压力传感器&#xff1a;利用压电材料的压电效应&#xff0c;当压力作用于压电材料时&#xff0c;会产生与压力成正比的电荷。通过测量电荷的大小&#xff0c;经过转换电路可得到对应的压力值。这种传感器响应速度快…

docker 安装 seafile 企业云盘

以下是 Docker 安装 Seafile 的详细步骤&#xff0c;涵盖基础安装和常用配置&#xff1a; 一、准备工作 安装 Docker 和 Docker Compose 确保系统已安装 Docker 和 Docker Compose。 创建工作目录 mkdir ~/seafile && cd ~/seafile二、使用官方简化镜像 Seafile 提供…

可狱可囚的爬虫系列课程 13:Requests使用代理IP

一、什么是代理 IP 代理 IP&#xff08;Proxy IP&#xff09;是一个充当“中间人”的服务器IP地址&#xff0c;用于代替用户设备&#xff08;如电脑、手机等&#xff09;直接与目标网站或服务通信。用户通过代理IP访问互联网时&#xff0c;目标网站看到的是代理服务器的IP地址&…