当前位置: 首页 > news >正文

高效的向量搜索算法——分层可导航小世界图(HNSW)

        最近在接触大模型相关内容,发现一种高效的向量搜索算法HNSW,这里做一下记录。

        在之前自己也接触过一段时间的复杂网络(网络科学),没想到,将网络科学的思想引入到向量搜索算法中,可以产生令人眼前一亮的成果。在网络科学中,小世界现象(Small World phenomenon)指的是一个网络中的节点(或者个体)之间通过短路径(较少的中间节点)相互连接的现象。具体来说,小世界网络具有以下几个特点:

  1. 短路径长度:尽管网络可能很大,但任意两个节点之间的路径长度通常都很短。这意味着,大多数节点可以通过较少的中间节点相互到达,这种路径长度通常比网络的直径(最长最短路径之间的最长距离)短得多。

  2. 高聚类系数:虽然路径长度很短,但网络中的节点通常会聚集在彼此的周围形成社团或群体。这些群体内的节点之间通常有更多的连接,形成高聚类系数。

  3. 随机连接:小世界网络通常具有随机连接的特性,即节点不仅连接到其直接邻居,还可能通过较远的节点间接连接到其他节点。这种随机性导致了网络中的短路径现象。

更通俗的解释:小世界现象最早由社会学家斯坦利·米尔格拉姆(Stanley Milgram)在他的“六度分隔”实验中观察到。在这个实验中,人们被要求通过将信件转发给自己认识的朋友来试图将信件传递给一个陌生人。结果显示,平均只需大约六次传递就可以将信件传递给目标个体,因此产生了“六度分隔”的概念。

        接下来,本文将会从该算法提出的业务背景、基础知识、原理分析、算法应用等方面进行阐述。

1. HNSW需要解决的业务问题

        相似性搜索是目前广泛被用到的一种基本技术,用于查找数据集中与给定查询“最接近”的数据点;由于这些搜索通常在向量空间中进行,因此也被称为“向量搜索”。这类搜索被系统广泛应用于自然语言处理、语音识别、图像/视频检索和推荐系统(等等);最近,它已成为生成式人工智能中提高性能的主要驱动因素之一,通过检索增强生成(RAG)。

        近似最近邻ANN 算法分为三大类:树、哈希和图。使用暴力搜索、基于树的结构(如 KD 树)和哈希(如局部敏感哈希)等方法,在大型高维数据集中的扩展性不佳,需要更有效的解决方案。而分层可导航小世界图(Hierarchical Navigable Small World, HNSW)在2016年被引入到学术视野中,逐步被业界证明是一种可行的技术解。HNSW通过引入层次(“层级”)来连接数据,类似于数字地图上添加“缩放”功能,能够解决在大型多维数据集中高效地找到最近邻居的问题。简而言之,HNSW 创建了一个多层次的图结构,其中每一层都是一个简化的、可导航的小世界网络。你可以将其类比为数字地图上的道路网络。放大地图,你可以看到城市和城镇通过主要道路连接。缩小到城市级别,你可以看到城市内部的人群如何相互连接。在最精细的缩放级别,你可以看到社区和社群之间的相互连接。

        HNSW 之所以重要,是因为它提供了一种在复杂的高维数据中导航和搜索的高效方法。这种结构允许更快速、更精确的最近邻居搜索,通过按层次组织数据并启用可导航的快捷方式,HNSW 显著减少了进行这些搜索所需的时间和计算资源,使其成为处理机器学习和人工智能中大型数据集的关键工具。HNSW是向量相似性搜索中表现最好的索引之一,具有超快的搜索速度和出色的召回率。

2. 基础知识

        HNSW中包含两种关键的基础技术:可导航小世界图以及概率跳表。利用基于图的数据结构,数据(节点)通过相似边相互连接,可以通过跟随这些边在图中进行导航。

2.1 可导航小世界(NSW)

        其思想是,如果我们构建一个邻近图,使其既有长距离链接又有短距离链接,那么搜索时间可以减少到(多项式/)对数复杂度。图中的每个顶点连接到几个其他顶点。称这些连接的顶点为朋友,每个顶点保持一个朋友列表,从而创建我们的图。在搜索 NSW 图时,可以从一个预定义的入口点开始。这个入口点连接到几个附近的顶点。确定这些顶点中哪个最接近我们的查询向量并移动到那里。

        可导航小世界的原理是,从任何一个节点出发,可以在少量“跳跃”中到达任何其他节点。在确定了一个入口点之后(可以是随机的、启发式算法),搜索相邻的节点,看看是否有比当前节点更近的节点。搜索移动到这些相邻节点中最接近的节点,并重复这一过程,直到没有更近的节点。

        在下面的示例中,我们正在搜索最接近“A”的节点,从节点“0”开始。该节点连接到三个节点,但最接近“A”的是节点“1”。然后我们从节点“1”重复这个过程,发现节点“2”比节点“1”更近,最后节点“3”比节点“2”更近。与节点“3”连接的节点都比“A”更远,因此我们确定节点“3”是最接近的节点:

        在构建图时,参数 M 设置了新节点与其最近邻居的边的数量(上面的示例使用 M=2)。更大的 M 意味着一个更互联、密度更大的图,但这将消耗更多的内存,并且插入速度更慢。随着节点被插入图中,系统搜索 M 个最近的节点并与这些节点建立双向连接。网络节点将具有不同程度的连接性,类似于社交网络中的用户,有些用户连接到成千上万(或数百万)的人,而其他用户仅连接到几百(或几十)的人。另一个 Mmax 参数决定了一个节点可以拥有的最大边数,因为过多的边会影响性能。稍后会重点介绍一下新增数据入图的过程,帮助理解。

        NSW 搜索的一个缺点是它总是选择通往“最近节点”的最短路径,而不考虑图的更广泛结构;这被称为“贪婪搜索”,有时可能会导致被困在局部最优或局部区域——这种现象被称为“过早停止”。考虑以下图中的情况,我们从不同的节点“0”进入,并且有一个不同的“A”节点:连接到节点“0”的两个节点都比“A”更远,因此算法确定“0”是最接近的节点。这种情况可能在导航过程中的任何点发生,而不仅仅是在起始节点。算法不会尝试探索任何更远的节点,因为它假设更远的节点会离得更远!

        有一些解决这种贪婪性带来的早停问题。可导航小世界模型被定义为任何使用贪婪路由且具有(多项式/)对数复杂度的网络。当图不可导航时,贪婪路由的效率在较大的网络(1-10K+ 顶点)中会下降。路由是由两个阶段组成。从“缩放”阶段开始,经过低度顶点(度是顶点具有的链接数)——然后是“缩小”阶段,经过高度顶点。高度顶点有很多链接,而低度顶点的链接很少。 停止条件是,在当前顶点的朋友列表中找不到更近的顶点。正因为如此,在缩放阶段(链接较少,不太可能找到更近的顶点),我们更有可能碰到局部最小值并过早停止。为了减少过早停止的概率(并提高召回率),我们可以增加顶点的平均度数,但这会增加网络的复杂性(和搜索时间)。因此,需要在召回率和搜索速度之间平衡顶点的平均度数,后续会看到需要设置节点连接数的参数M、Mmax等。另一种方法是从高阶顶点开始搜索(先缩小)。对于 NSW 来说,这确实能提高低维数据的性能。这也是 HNSW 结构中的一个重要因素。

2.2 概率跳表

        概率跳表允许像排序数组一样快速搜索,同时使用链表结构进行轻松(且快速)的新元素插入。跳跃表的结构允许插入和查询操作在平均 O(log n) 的时间内完成——操作时间随着数据量的增加呈对数增长,而不是线性、多对数或更糟。在跳跃表的底层,每个节点都按顺序连接到下一个节点。随着更多层的添加,序列中的节点会被“跳过”。以下是维基百科中的插图,展示了如何将十个节点组织成一个四层跳跃表:

        跳表通过构建多个链表层来工作。在第一层中,找到跳过许多中间节点/顶点的链接。当向下移动层次时,每个链接的“跳过”数量会减少。要搜索跳表,从具有最长“跳过”的最高层开始,并沿着边向右(下方)移动。如果发现当前节点的“键”大于正在搜索的键——已经超过了目标,因此向下移动到下一层中的前一个节点。

        跳跃表通常是以概率方式构建的——节点出现在更高层的概率降低,这意味着更高层的节点较少。当搜索一个节点时,算法从顶层的“头部”进入,并继续前进到大于或等于目标的下一个元素。在这一点上,搜索元素已找到,或者它会下降到下一层(更细粒度的层),并重复这一过程,直到找到搜索节点,或者找到搜索节点的相邻节点。在跳跃表和相似性搜索的上下文中,“大于”意味着“更相似”——例如,在余弦相似度的情况下,最接近的相似度为1。        

        插入操作涉及确定节点进入结构的层,查找到第一个“大于”节点,但同时记录下“大于”节点之前的最后一个节点。一旦找到那个“大于”节点,它会更新最后一个节点指向新节点,并将新节点指向“大于”节点。然后它继续到下一层,直到节点出现在所有层中。删除操作遵循类似的逻辑来定位和更新指针。

        概率跳表结构中,从顶层开始。如果当前键大于正在搜索的键(或者到达末尾),就下到下一层。

        HNSW 继承了相同的分层格式,在最高层具有较长的边(用于快速搜索),在较低层具有较短的边(用于精确搜索)。将层次结构添加到 NSW 中会产生一个跨不同层次分离链接的图。在顶层,我们有最长的链接,而在底层,我们有最短的链接。

        HNSW 的分层图,顶层是入口点,只包含最长的链接,随着向下移动层次,链接的长度变得更短且数量更多。 在搜索过程中,进入顶层,在这里找到最长的链接。这些顶点往往是高阶顶点(链接分布在多个层次上),这意味着默认情况下,从 NSW 所描述的缩小阶段开始。在每一层中遍历边缘,就像在 NSW 中一样,贪婪地移动到最近的顶点,直到找到一个局部最小值。与 NSW 不同的是,此时转移到当前顶点的下一层并重新开始搜索。重复这一过程,直到找到最底层——第0层的局部最小值。

        HNSW 将跳跃表的快速遍历与 NSW 的丰富互连性相结合,并添加了层次结构以增强搜索效率。HNSW 的核心是其分层架构,它将数据组织成相互连接的节点层,允许快速跳过不必要的计算,专注于相关的搜索区域。这种层次化的方法,加上小世界概念,优化了通往高精度结果的路径,对于需要快速准确检索的应用程序至关重要。

3. HNSW索引构建与搜索

3.1 构建层次结构

        构建层次结构 HNSW 的基础在于其层次结构。构建这个多层图从代表整个数据集的基础层开始。随着层层上升,每个后续层作为下层的简化概览,包含更少的节点,并充当允许跨图更长跳跃的快速通道。HNSW 中的概率分层与跳跃表类似,由通常表示为 mL 的参数控制。该参数决定了节点出现在连续层中的可能性,随着层级上升,概率呈指数下降。正是这种概率的指数衰减在更高层级上创造了一个逐渐稀疏且更易导航的结构,确保了图在搜索操作中的效率。

3.2 构建可导航图 

        在 HNSW 的每一层中构建可导航图是决定算法有效性的关键。参数 M 和 Mmax在此过程中起着至关重要的作用。新的参数 Mmax0 专用于基础层,允许该基础层比更高层具有更大的连通性。HNSW 还添加了 efConstruction 参数,该参数确定在添加新节点时动态候选列表的大小。较高的 efConstruction 值意味着建立连接的过程更彻底,但速度较慢。该参数有助于平衡连接密度,影响搜索的效率和准确性。这一阶段的一个重要部分是高级邻居选择启发式方法,这是 HNSW 的关键创新。该启发式方法不仅仅将新节点链接到其最近的邻居,还识别出能够改善整个图连通性的节点。这种方法在连接不同的数据簇时特别有效,创建了图中的关键快捷方式。如 HNSW 论文中所示,这种启发式方法促进了纯粹基于邻近性的方法可能忽略的连接,显著增强了图的可导航性:

        通过战略性地平衡连接密度和采用智能邻居选择,HNSW 构建了一个多层图结构,不仅对即时搜索有效,而且对未来的搜索需求也是最优结构。HNSW 在管理复杂的高维数据空间中表现出色,成为相似性搜索任务中的宝贵工具。

3.3 插入与删除操作

3.3.1 插入操作

        HNSW 的创建者发现,当最小化各层之间共享邻居的重叠时,可以实现最佳性能。减少 m_L 可以帮助最小化重叠(将更多向量推向第0层),但这会增加搜索期间的平均遍历次数。因此,使用一个平衡两者的 m_L 值。一个经验法则是这个最优值为 1/ln(M)。

        图构建从顶层开始。进入图后,算法贪婪地遍历边缘,找到与插入向量 q 最近的 ef 个邻居——此时 ef = 1。找到局部最小值后,它会下移到下一层(就像在搜索期间一样)。这个过程重复进行,直到达到我们选择的插入层。此时开始构建的第二阶段。ef 值增加到 efConstruction(设定的参数),这意味着会返回更多的最近邻居。在第二阶段,这些最近邻居是新插入元素 q 的链接候选项,并作为进入下一层的入口点。从这些候选项中添加 M 个邻居作为链接——最简单的选择标准是选择最接近的向量。经过多次迭代后,添加链接时还需要考虑两个参数M_max 和M_max0 。

        M_max 定义了一个顶点可以拥有的最大链接数,M_max0 则定义了第0层顶点的最大链接数。插入从所选层开始,将新节点连接到该层内最接近的 M 个邻居。如果邻居节点的连接数超过 Mmax,则移除多余的连接。然后以类似的方式将节点插入下一层,直到参考 Mmax0 作为最大连接数的底层。

        HNSW 确定新数据插入的层是通过一个随机过程来实现的,主要依赖于两个关键参数:M 和 mL。这里简要说明一下:

  1. M 参数:决定了每个节点在插入时连接的最近邻数量。这决定了在每层上节点的平均连接密度。

  2. mL 参数:它是一个与层级相关的参数,用于控制节点在不同层级上出现的概率。随着层级的增加,节点在更高层级出现的概率会指数级地减少。

        HNSW 通过使用概率方法来选择节点的插入层级,以便在构建图形时确保较高层级上的稀疏性,从而优化搜索效率。在 HNSW 中,插入新节点并构建连接的过程是通过节点之间的相似度或距离来确定它们之间的连接关系。具体来说:

  1. 相似度度量:通常使用的是向量之间的余弦相似度或欧氏距离。余弦相似度是常见的用于衡量向量之间相似程度的指标,特别适用于高维空间中的向量。

  2. 层级内的连接:在 HNSW 的每个层级中,当插入新节点时,算法会根据其与现有节点的相似度选择最近的 M 个邻居节点,然后建立双向连接。这些连接不是固定的,而是在搜索时动态调整的,以便在高维空间中有效地导航和搜索。

        因此,HNSW 在插入新节点并构建连接时,通过相似度度量来确定节点之间的连接关系,以确保图形在高维向量空间中的有效性和可导航性。插入操作的停止条件是达到0层的局部最小值,这意味着当在0层中无法找到更近的邻居时,插入过程结束。

3.3.2 删除操作

        从 HNSW 图中删除节点的过程涉及在层次结构的多个层中仔细更新其邻居的连接。这对于保持图的结构完整性和可导航性至关重要。不同的 HNSW 实现可能采用各种策略,以确保每层内的连接保持不变,并且没有节点被孤立或从图中断开。这种方法确保了图在最近邻搜索中的持续效率和可靠性。

总之,HNSW 中的插入和删除过程设计得既高效又能保持图的层次和可导航性,这对于算法快速准确的最近邻搜索能力至关重要。这些过程突显了 HNSW 的适应性,使其成为动态高维相似性搜索应用中的鲁棒选择。

3.4 搜索过程

        在 HNSW 中搜索涉及穿越层次结构,从最顶层开始,逐层下降到底层,目标的最近邻居位于底层。该算法利用结构的分层优势——使用较高层进行快速粗粒度探索,较低层进行详细搜索——优化通往最相似节点的路径。启发式方法,如选择最佳入口点和最小化跳跃次数,被用来加速搜索,而不影响结果的准确性。

        寻找节点 “A” 的最近邻居时,首先选择位于最顶层的 Layer 2 的一个节点,并像在 NSW 中一样导航到其最近的邻居节点 “1”。此时,下降一层到 Layer 1,搜索这一层是否有更近的邻居,找到的节点是 “2”。最终下降到底层 Layer 0,发现节点 “3” 是最近的:

        在 HNSW 中的搜索与在 NSW 中的搜索相似,但增加了“层”的维度,使得可以快速修剪图表,仅保留相关的邻居。


4. HNSW的实现

4.1 构建HNSW索引

        使用Facebook AI的相似性搜索库(Faiss)来实现HNSW,并测试不同的构建和搜索参数,以观察这些参数如何影响索引性能。

要初始化HNSW索引,编写如下代码:

# setup our HNSW parameters
d = 128  # vector size
M = 32index = faiss.IndexHNSWFlat(d, M)
print(index.hnsw)

        到此为止,已经设置了 M 参数,即在插入时添加到每个顶点的邻居数,但我们还缺少 M_max 和 M_max0。在 Faiss 中,这两个参数在索引初始化时通过调用 set_default_probas 方法自动设置。M_max 值被设置为 M,而 M_max0 被设置为 M*2。

        在通过 index.add(xb) 构建索引之前,会发现层(或在 Faiss 中的级别)数量尚未设置:

# the HNSW index starts with no levels
index.hnsw.max_level

-1

# and levels (or layers) are empty too
levels = faiss.vector_to_array(index.hnsw.levels)
np.bincount(levels)
array([], dtype=int64)

        如果继续构建索引,会发现这两个参数现在都已经设置好了。

index.add(xb)
# after adding our data we will find that the level
# has been set automatically
index.hnsw.max_level

4

# and levels (or layers) are now populated
levels = faiss.vector_to_array(index.hnsw.levels)
np.bincount(levels)
array([     0, 968746,  30276,    951,     26,      1], dtype=int64)

        这里可以看到我们的图中有多少层,从 0 -> 4,这由 max_level 描述。而 levels 显示了从 0 到 4 层(忽略第一个 0 值)每一层上的顶点分布。甚至可以找到哪个向量是我们的入口点:

index.hnsw.entry_point

        这是对 Faiss 风格的 HNSW 图的高层视图,但在测试索引之前,深入了解一下 Faiss 是如何构建这个结构的。

        当初始化索引时,传入向量的维度 d 和每个顶点的邻居数 M。这调用了方法 'set_default_probas',并传入了 M 和 1 / log(M) 作为 levelMult(相当于上面的 m_L)。这个方法的 Python 等效代码如下:

def set_default_probas(M: int, m_L: float):nn = 0  # set nearest neighbors count = 0cum_nneighbor_per_level = []level = 0  # we start at level 0assign_probas = []while True:# calculate probability for current levelproba = np.exp(-level / m_L) * (1 - np.exp(-1 / m_L))# once we reach low prob threshold, we've created enough levelsif proba < 1e-9: breakassign_probas.append(proba)# neighbors is == M on every level except level 0 where == M*2nn += M*2 if level == 0 else Mcum_nneighbor_per_level.append(nn)level += 1return assign_probas, cum_nneighbor_per_level

        正在构建两个向量 — assign_probas,表示在给定层次上插入的概率,以及 cum_nneighbor_per_level,表示在不同插入层次上分配给顶点的累积最近邻数。

assign_probas, cum_nneighbor_per_level = set_default_probas(32, 1/np.log(32)
)
assign_probas, cum_nneighbor_per_level

([0.96875,

0.030273437499999986,

0.0009460449218749991,

2.956390380859371e-05,

9.23871994018553e-07,

2.887099981307982e-08],

[64, 96, 128, 160, 192, 224])

        从这里可以看出,在级别0插入向量的概率显著高于较高级别。这个函数意味着较高级别更稀疏,降低了“卡住”的可能性,并确保我们从更长的范围遍历开始。

         assign_probas 向量被另一个名为 random_level 的方法使用 — 在这个函数中,每个顶点被分配一个插入级别。

def random_level(assign_probas: list, rng):# get random float from 'r'andom 'n'umber 'g'eneratorf = rng.uniform() for level in range(len(assign_probas)):# if the random float is less than level probability...if f < assign_probas[level]:# ... we assert at this levelreturn level# otherwise subtract level probability and try againf -= assign_probas[level]# below happens with very low probabilityreturn len(assign_probas) - 1

        使用NumPy的随机数生成器rng(在下面初始化)生成一个随机浮点数f。对于每个级别,检查f是否小于assign_probas中分配给该级别的概率 — 如果是,那就是插入层级。

        如果f太高,从f中减去assign_probas的值,并尝试下一个级别。这个逻辑的结果是,向量最有可能被插入到级别0。如果不是,每增加一个级别,插入概率会递减。

        最后,如果没有级别满足概率条件,将向量插入到最高级别,即返回 len(assign_probas) - 1。如果比较我们的Python实现和Faiss的分布,我们会看到非常相似的结果。

chosen_levels = []
rng = np.random.default_rng(12345)
for _ in range(1000000):chosen_levels.append(random_level(assign_probas, rng))np.bincount(chosen_levels)
array([968821,  30170,    985,     23,       1],dtype=int64)

        在Faiss实现(左)和Python实现(右)中各层级上顶点的分布。 Faiss实现还确保我们始终在最高层有至少一个顶点作为图的入口点。

4.2 HNSW性能

       接下来观察不同参数对我们的召回率、搜索和构建时间以及内存使用的影响。将修改三个参数:M、efSearch和efConstruction。将对Sift1M数据集进行索引,可以使用此脚本下载和准备数据。

        像之前一样,初始化索引如下:

index = faiss.IndexHNSWFlat(d, M)

        初始化索引后可以修改的另外两个参数是 efConstruction 和 efSearch。

index.hnsw.efConstruction = efConstruction
index.add(xb)  # build the index
index.hnsw.efSearch = efSearch
# and now we can search
index.search(xq[:1000], k=1)

        必须在通过 index.add(xb) 构建索引之前设置 efConstruction 值,但是 efSearch 可以在搜索之前的任何时候设置。

首先看一下召回性能。

        不同 M、efConstruction 和 efSearch 参数下的 Recall@1 性能。 较高的 M 和 efSearch 值可以显著提高召回性能 — 同时也明显需要合理的 efConstruction 值。还可以增加 efConstruction 以在较低的 M 和 efSearch 值下实现更高的召回率。

        然而,这种性能并非毫无代价。与往常一样,需要在召回率和搜索时间之间进行权衡 。

        在搜索1000个查询时,不同 M、efConstruction 和 efSearch 参数下的搜索时间,单位为微秒(µs)。注意,y 轴使用对数刻度。 虽然较高的参数值可以提供更好的召回率,但对搜索时间的影响可能非常显著。在这里,搜索1000个相似向量(xq[:1000]),召回率/搜索时间可以从80%-1毫秒到100%-50毫秒不等。如果对召回率要求不高,搜索时间甚至可以达到0.1毫秒。

关于efConstruction和efSearch参数,做一下说明:

在HNSW(Hierarchical Navigable Small World)索引中,efConstructionefSearch是两个重要的参数:

  1. efConstruction

    • efConstruction是用来控制构建索引时的动态候选列表的大小。在构建索引过程中,为了确定节点的连接关系,HNSW算法需要动态地选择与新节点进行连接的候选节点。efConstruction定义了这个动态候选列表的大小。
    • 较大的efConstruction值会导致更大的动态候选列表,这可能会增加索引构建的时间,因为算法需要在更多的节点中进行选择。但是,它可以帮助改善索引的质量,尤其是在处理高维数据时,能够更好地确保节点之间的连接质量和图结构的稳定性。
  2. efSearch

    • efSearch是在搜索阶段用来控制搜索时的动态候选列表的大小。在进行查询时,HNSW算法会根据查询点的相似度动态地调整候选列表的大小,以加快搜索速度并且保证搜索结果的质量。
    • 较大的efSearch值会导致更大的动态候选列表,在搜索时可能会增加计算量,但通常会提高搜索结果的召回率。相反,较小的efSearch值可能会加快搜索速度,但可能牺牲搜索结果的质量。

总结来说,efConstructionefSearch都是在HNSW索引中用来控制动态候选列表大小的参数,分别影响索引的构建过程和查询过程中的性能和效果。

        如果查询大多数情况下是低频率的,那么增加 efConstruction 是一个很好的选择。它可以在几乎不影响搜索时间的情况下提高召回率,特别是在使用较低的 M 值时。

        在仅搜索一个查询时的 efConstruction 和搜索时间。使用较低的 M 值时,不同 efConstruction 值的搜索时间几乎保持不变。 这一切看起来都很不错,但是 HNSW 索引的内存使用情况如何呢?在这方面可能会稍显不尽人意。

        使用 Sift1M 数据集,随着 M 值的增加,内存使用情况也在增加。efSearch 和 efConstruction 对内存使用没有影响。 efConstruction 和 efSearch 均不影响索引的内存使用,因此只需考虑 M 值。即使将 M 值设为较低的 2,索引大小也已超过 0.5GB,在 M 值为 512 时接近 5GB。HNSW 在内存利用方面并不是最佳的索引方式。然而,如果这一点很重要且使用其他索引不是选项,可以通过使用产品量化(PQ)来改善。使用 PQ 将降低召回率并增加搜索时间。

        另外,很多场景也可能将HNSW与IVF结合使用,提升整体的检索性能和效率。

IVF(Inverted File)是一种用于高效向量检索的数据结构,通常用于加速近似最近邻搜索(Approximate Nearest Neighbor Search,ANN)。它结合了倒排索引的思想和空间划分的方法,特别适用于处理大规模高维向量数据集。

主要思想是将向量空间划分为多个子空间(或者称为桶),每个桶中存储一组向量,通常使用一种聚类方法(如k-means)对数据集进行划分。在搜索时,首先根据查询向量的位置定位到对应的桶,然后只需在这个桶内进行精确或近似的最近邻搜索,避免了对整个数据集进行线性扫描,从而显著提高了检索效率。

IVF的优点包括:

  1. 减少搜索空间:通过空间划分,将搜索范围缩小到少数几个桶,极大地减少了需要搜索的向量数目。
  2. 高效的近似搜索:在每个桶内进行近似搜索,结合了高效的数据结构和查询算法,可以快速返回最相似的向量。

5. HNSW优劣势

        HNSW 的优缺点 和大多数技术一样,尽管 HNSW 在向量搜索领域带来了诸多改进,但它也有一些不足,可能不适用于所有用例。

HNSW 的优点

  • 高效的搜索性能:HNSW 在高维空间中显著优于传统方法,如 KD 树和暴力搜索,以及 NSW,使其成为大规模数据集的理想选择。
  • 可扩展性:该算法随着数据集的增长而保持其效率,表现出良好的可扩展性。
  • 层次结构:多层图结构允许快速浏览数据集,通过跳过无关数据部分实现更快的搜索。
  • 鲁棒性:HNSW 在各种类型的数据集(包括高维数据集)中表现出色,而这些数据集通常对其他算法构成挑战​​。

HNSW 的缺点

  • 内存使用:虽然 HNSW 的图结构在搜索操作中效率很高,但可能会消耗大量内存,尤其是在每个节点有大量连接的情况下。这对图的大小提出了实际限制,因为操作必须在内存中执行才能快速。
  • 实现复杂性:与更简单的结构相比,该算法的层次和概率性质使其实现和优化更加复杂。
  • 参数敏感性:HNSW 的性能可能对其参数(如每个节点的连接数)敏感,需要仔细调整以获得最佳结果。
  • 动态环境中的潜在开销:虽然 HNSW 在静态数据集中的效率很高,但在动态环境中维护图结构(频繁添加或删除数据点)的开销可能很大。

6. 参考资料

【1】Malkov, Yu A., and Dmitry A. Yashunin. "Efficient and robust approximate nearest neighbor search using hierarchical navigable small world graphs." IEEE transactions on pattern analysis and machine intelligence 42.4 (2018): 824-836.

【2】Understanding Hierarchical Navigable Small Worlds (HNSW)

【3】Hierarchical Navigable Small Worlds (HNSW)

相关文章:

高效的向量搜索算法——分层可导航小世界图(HNSW)

最近在接触大模型相关内容&#xff0c;发现一种高效的向量搜索算法HNSW&#xff0c;这里做一下记录。 在之前自己也接触过一段时间的复杂网络&#xff08;网络科学&#xff09;&#xff0c;没想到&#xff0c;将网络科学的思想引入到向量搜索算法中&#xff0c;可以产生令人眼前…...

【MySQL备份】Percona XtraBackup全量备份实战篇

目录 1. 前言 2.准备工作 2.1.环境信息 2.2.创建备份目录 2.3.配置/etc/my.cnf文件 2.4.授予root用户BACKUP_ADMIN权限 3.全量备份 4.准备备份 5.数据恢复 6.总结 "实战演练&#xff1a;利用Percona XtraBackup执行MySQL全量备份操作详解" 1. 前言 本文…...

港口危险货物安全管理人员考试题库(含答案)

一、单选题 1.化学品安全标签内容中警示词有( )种分别进行危害程度的警示。 A、3 B、4 C、5 参考答案:A 2.运输放射性物品&#xff0c;应当使用( )的放射性物品运输包装容器(以下简称运输容器)。 A、专业 B、专用 C、统一 D、定制 参考答案:B 3.库区仪表及计算机监控管理系…...

什么是 JVM( Java 虚拟机),它在 Java 程序执行中扮演什么角色?

JVM&#xff0c;全称Java Virtual Machine&#xff0c;中文译作“Java虚拟机”&#xff0c;它是运行Java程序的软件环境&#xff0c;也是Java语言的核心部分之一。 想象一下&#xff0c;如果你是一位环球旅行家&#xff0c;每到一个新的国家&#xff0c;都需要学习当地的语言才…...

Python容器 之 列表--下标和切片

列表的切片 得到是 新的列表字符串的切片 得到是 新的字符串 如果下标 不存在会报错 list1 [1, 3.14, "hello", False] print(list1)# 获取 列表中 第一个数据 print(list1[0]) # 1# 获取列表中的最后一个数据 print(list1[-1]) # [False]# 获取中间两个数 即 3.1…...

Docker 运行Nacos无法访问地址解决方法

参考我的上一篇文章去配置好镜像加速器&#xff0c;镜像加速器不是配置越多越好&#xff0c;重试次数多了会失败 Dockerhub无法拉取镜像配置阿里镜像加速器-CSDN博客 错误的尝试 最开始按照网上的方式去配了一大堆&#xff0c;发现下不下来。 镜像源地址&#xff1a;https:…...

Stable Diffusion 商业变现与绘画大模型多场景实战

前言 ai绘画软件Stable Diffusion是一种通过模拟扩散过程&#xff0c;将噪声图像转化为目标图像的文生图模型&#xff0c;具有较强的稳定性和可控性&#xff0c;可以将文本信息自动转换成高质量、高分辨率且视觉效果良好、多样化的图像。在日常工作中&#xff0c;ai绘画软件St…...

[CocosCreator]CocosCreator网络通信:https + websocket + protobuf

环境 cocos creator版本&#xff1a;3.8.0 开发语言&#xff1a;ts 操作系统&#xff1a;windows http部分 直接使用 XMLHttpRequest 创建http请求 // _getHttpUrl 方法自己写字符串拼接public httpPostJsonRequest(uri: string, headData: any, data: any, cb: Function…...

并发控制-事务的调度、数据不一致问题(更新丢失、脏读、不可重复读)、非串行调度的的可串行化

一、引言 1、数据库管理系统DBMS的事务处理技术实现的另一个主要功能部分是并发控制机制。并发控制机制完成的功能就是对并发执行的事务进行控制&#xff0c;保证事务的隔离性&#xff0c;从而进一步保持数据库的一致性。 2、事务的并发控制就是对并发执行的不同事务中的数据…...

Golang | Leetcode Golang题解之第202题快乐数

题目&#xff1a; 题解&#xff1a; func isHappy(n int) bool {cycle : map[int]bool{4: true, 6: true, 37: true, 58: true, 89: true, 145: true, 42: true, 20: true}for n ! 1 && !cycle[n] {n step(n)}return n 1 }func step(n int) int {sum : 0for n > …...

算法:哈希表

目录 题目一&#xff1a;两数之和 题目二&#xff1a;判定是否互为字符重排 题目三&#xff1a;存在重复元素I 题目四&#xff1a;存在重复元素II 题目五&#xff1a;字母异位词分组 关于哈希表 哈希表就是存储数据的容器 哈希表的优势是&#xff1a;快速查找某个元素O(…...

自然语言处理基本知识(1)

一 分词基础 NLP:搭建了计算机语言和人类语言之间的转换 1 精确分词&#xff0c;试图将句子最精确的分开&#xff0c;适合文本分析 >>> import jieba >>> content "工信处女干事每月经过下属科室" >>> jieba.cut(content,cut_all …...

Java中的数据加密与安全传输

Java中的数据加密与安全传输 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们来探讨一下在Java中如何实现数据加密与安全传输。 随着互联网的普及和网络…...

UG NX二次开发(C++)-根据草图创建拉伸特征(UFun+NXOpen)

1、前言 UG NX是基于特征的三维建模软件,其中拉伸特征是一个很重要的特征,有读者问如何根据草图创建拉伸特征,我在这篇博客中讲述一下草图创建拉伸特征的UG NX二次开发方法,感兴趣的可以加入QQ群:749492565,或者在评论区留言。 2、在UG NX中创建草图,然后创建拉伸特征 …...

TS_开发一个项目

目录 一、编译一个TS文件 1.安装TypeScript 2.创建TS文件 3.编译文件 4.用Webpack打包TS ①下载依赖 ②创建文件 ③启动项目 TypeScript是微软开发的一个开源的编程语言&#xff0c;通过在JavaScript的基础上添加静态类型定义构建而成。TypeScript通过TypeScript编译器或…...

2024年华为OD机试真题-传递悄悄话 -C++-OD统一考试(C卷D卷)

2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集) 题目描述: 给定一个二叉树,每个节点上站着一个人,节点数字表示父节点到该节点传递悄悄话需要花费的时间。 初始时,根节点所在位置的人有一个悄悄话想要传递给其他人,求二叉树所有节…...

eclipse基础工程配置( tomcat配置JRE环境)

文章目录 I eclipse1.1 工程配置1.2 编译工程1.3 添加 JRE for the project build pathII tomcat配置JRE环境2.1 Eclipse编辑tomcat运行环境(Mac版本)2.2 Eclipse编辑tomcat运行环境(windows版本)2.3 通过tomcat7W.exe配置运行环境(windows系统)I eclipse 1.1 工程配置 …...

Spring Boot 学习第八天:AOP代理机制对性能的影响

1 概述 在讨论动态代理机制时&#xff0c;一个不可避免的话题是性能。无论采用JDK动态代理还是CGLIB动态代理&#xff0c;本质上都是在原有目标对象上进行了封装和转换&#xff0c;这个过程需要消耗资源和性能。而JDK和CGLIB动态代理的内部实现过程本身也存在很大差异。下面将讨…...

Linux[高级管理]——Squid代理服务器的部署和应用(传统模式详解)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f468;‍&#x1f4bb;Linux高级管理专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月24日11点11分 &#x1f004;️文章质量&#xff1a;95分 目录 ————前言———— Squid功能 Squ…...

使用Vue 2 + Element UI搭建后台管理系统框架实战教程

后台管理系统作为企业内部的核心业务平台&#xff0c;其界面的易用性和功能性至关重要。Vue 2作为一个成熟的前端框架&#xff0c;以其轻量级和高效著称&#xff0c;而Element UI则是一套专为桌面端设计的Vue 2组件库&#xff0c;它提供了丰富的UI元素和组件&#xff0c;大大简…...

Carla安装教程

1.前言 对于从事自动驾驶的小伙伴而言&#xff0c;或多或少应该都接触过一些的仿真软件&#xff0c;今天要给大家介绍的这款仿真软件应该算的上是业界非常有名的一款仿真软件——carla。 目前carla的学习教程也还是蛮多的&#xff0c;但是写的都不是很全&#xff0c;在配置的…...

【PYG】处理Cora数据集分类任务使用的几个函数log_softmax,nll_loss和argmax

文章目录 log_softmax解释作用示例解释输出 nll_loss解释具体操作示例代码解释 nll_losslog_softmaxcross_entropy解释代码示例解释 argmax()解释作用示例代码解释示例输出 log_softmax F.log_softmax(x, dim1) 是 PyTorch 中的一个函数&#xff0c;用于对输入张量 x 应用 log…...

Labview绘制柱状图

废话不多说&#xff0c;直接上图 我喜欢用NXG风格&#xff0c;这里我个人选的是xy图。 点击箭头指的地方 选择直方图 插值选择第一个 直方图类型我选的是第二个效果如图。 程序部分如图。 最后吐槽一句&#xff0c;现在看CSDN好多文章都要收费了&#xff0c;哪怕一些简单的入…...

使用Python实现一个简单的密码管理器

文章目录 一、项目概述二、实现步骤2.1 安装必要的库2.2 设计密码数据结构2.3 实现密码加密和解密2.4 实现主要功能2.4.1 添加新密码2.4.2 显示所有密码2.4.3 查找特定密码2.4.4 更新密码2.4.5 删除密码 2.5 实现用户界面 三、代码示例3.1 加密和解密示例3.2 用户界面示例 在现…...

【云原生】服务网格(Istio)如何简化微服务通信

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《未来已来&#xff1a;云原生之旅》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、微服务架构的兴起 2、Istio&#xff1a;服务网格的佼…...

spring boot 整合 sentinel

注意版本问题 我这是jdk11 、spring boot 2.7.15 、 alibaba-sentinel 2.1.2.RELEASE <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.15</version><…...

蜜雪冰城小程序逆向

app和小程序算法一样 小程序是wasm...

pbootcms提交留言成功后跳转到指定的网址

pbootcms在线留言表单提交成功后&#xff0c;如何跳转到指定的网址&#xff0c;默认提交留言后留在原来的页面&#xff0c;如果提交后需要跳转到指定网址&#xff0c;我们需要对文件进行修改。首先我们打开/core-/function/helper.php文件找到第162行左右代码&#xff1a; ech…...

16、matlab求导、求偏导、求定积分、不定积分、数值积分和数值二重积分

0&#xff09;前言 在MATLAB中&#xff0c;对函数进行不同形式的求导、求积分操作是非常常见的需求&#xff0c;在工程、科学等领域中经常会用到。以下是关于求导、求积分以及数值积分的简介&#xff1a; 求导&#xff1a;在MATLAB中可以使用diff函数对函数进行求导操作。diff…...

MySQL 9.0创新版发布!功能又进化了!

作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验&#xff0c; Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯及Greenplum备份恢复&#xff0c; 安装迁移&#xff0c;性能优化、故障…...

后端系统的安全性

后端系统的安全性 后端系统的安全性是任何Web应用或服务的核心组成部分&#xff0c;它涉及保护数据、用户隐私以及系统免受恶意攻击。以下是后端安全的一些关键点&#xff1a; 认证和授权&#xff1a;确保只有经过身份验证的用户才能访问特定资源。这通常包括使用用户名/密码…...

.net 百度翻译接口核心类

百度翻译api &#xff1a;http://developer.baidu.com/wiki/index.php?title帮助文档首页/百度翻译/翻译AP 核心翻译类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Newtonsoft.Json; using System.Net; using System.I…...

安卓应用开发学习:通过腾讯地图SDK实现定位功能

一、引言 这几天有些忙&#xff0c;耽误了写日志&#xff0c;但我的学习始终没有落下&#xff0c;有空我就会研究《 Android App 开发进阶与项目实战》一书中定位导航方面的内容。在我的手机上先后实现了“获取经纬度及地理位置描述信息”和“获取导航卫星信息”功能后&#x…...

iptable精讲

SNAT策略 SNAT策略的典型应用环境 局域网主机共享单个公网IP地址接入Internet SNAT策略的原理 源地址转换&#xff0c;Source Network Address Translantion 修改数据包的源地址 部署SNAT策略 1.准备二台最小化虚拟机修改主机名 主机名&#xff1a;gw 主机名&#xff1…...

2024 年如何构建 AI 软件

人工智能 (AI) 是当今 IT 行业最热门的话题&#xff0c;受到大型科技公司、大型企业和投资者的青睐。如果有人不参与 AI&#xff0c;他们就出局了。虽然“AI 泡沫”一词尚未公开使用&#xff0c;但街上的每个人都可能听说过 AI 将取代我们的工作&#xff08;可能不会&#xff0…...

Python实战,桌面小游戏,剪刀石头布

注意:本文的下载教程,与以下文章的思路有相同点,也有不同点,最终目标只是让读者从多维度去熟练掌握本知识点。 下载教程: Python项目开发实战_桌面小游戏-剪刀石头布_编程案例解析实例详解课程教程.pdf 创建一个基于Python的桌面小游戏“剪刀石头布”是一个很好的编程实践…...

Hadoop权威指南-读书笔记-01-初识Hadoop

Hadoop权威指南-读书笔记 记录一下读这本书的时候觉得有意思或者重要的点~ 第一章—初识Hadoop Tips&#xff1a; 这个引例很有哲理嘻嘻&#x1f604;&#xff0c;道出了分布式的灵魂。 1.1 数据&#xff01;数据&#xff01; 这一小节主要介绍了进入大数据时代&#xff0c;面…...

HttpServletResponse设置headers返回,发现headers中缺少“Content-Length“和“Content-Type“两个参数。

业务中需要将用httpUtils请求返回的headers全部返回&#xff0c;塞到HttpServletResponse中&#xff0c;代码如下&#xff1a; HttpServletResponse response;// 返回headers Arrays.stream(httpResponse.getHeaders()).forEach(header -> response.setHeader(header.getNa…...

GraphPad Prism生物医学数据分析软件下载安装 GraphPad Prism轻松绘制各种图表

Prism软件作为一款功能强大的生物医学数据分析与可视化工具&#xff0c;其绘图功能尤为突出。该软件不仅支持绘制基础的图表类型&#xff0c;如直观明了的柱状图、展示数据分布的散点图&#xff0c;以及描绘变化趋势的曲线图&#xff0c;更能应对复杂的数据呈现需求&#xff0c…...

7/1 uart

uart4.c #include "uart4.h"//UART4_RX > PB2 //UART4_TX > PG11char rebuf[51] {0}; //rcc/gpio/uart4初始化 void hal_uart4_init() {/********RCC章节初始化*******///1.使能GPIOB组控制器 MP_AHB4ENSETR[1] 1RCC->MP_AHB4ENSETR | (0x1 << 1)…...

zdppy_api+vue3+antd开发前后端分离的预加载卡片实战案例

后端代码 import api import upload import timesave_dir "uploads"async def rand_content(request):key api.req.get_query(request, "key")time.sleep(0.3)return api.resp.success(f"{key} " * 100)app api.Api(routes[api.resp.get(&qu…...

别小看手机导航,这些隐藏功能大部分人可能都不知道

在科技日新月异的今天&#xff0c;手机导航已经成为我们日常生活中不可或缺的一部分。它不仅仅是指引我们前往目的地的工具&#xff0c;更隐藏着许多黑科技功能&#xff0c;极大地丰富了我们的出行体验。 今天&#xff0c;让我们一起探索手机导航中那些鲜为人知却大有用处的隐…...

Lua实现链表(面向对象应用)

Lua实现面向对象 面向对象核心三要素Lua面向对象大致原理面向对象示例继承与多态示例 面向对象核心三要素 1.封装&#xff1a;对一个事物的抽象为一些属性和行为动作的集合&#xff0c;封装将属性和行为动作&#xff08;操作数据的方法&#xff09;绑定在一起&#xff0c;并隐藏…...

每隔一个小时gc一次的问题

原文地址https://www.cnblogs.com/jiangxinlingdu/p/7581064.html 设置一下这个 -XX:ExplicitGCInvokesConcurrent 或 -XXExplicitGCInvokesConcurrentAndUnloadsClasses 并且检查一下&#xff0c;并下面的值设置变大 java.rmi.dgc.leaseValue sun.rmi.dgc.client.gcInterv…...

VBA数据库解决方案第十二讲:如何判断数据库中数据表是否存在

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…...

五、Spring IoCDI ★ ✔

5. Spring IoC&DI 1. IoC & DI ⼊⻔1.1 Spring 是什么&#xff1f;★ &#xff08;Spring 是包含了众多⼯具⽅法的 IoC 容器&#xff09;1.1.1 什么是容器&#xff1f;1.1.2 什么是 IoC&#xff1f;★ &#xff08;IoC: Inversion of Control (控制反转)&#xff09;总…...

计算机网络八股文

计算机网络体系架构&#xff1f; OSI结构&#xff1a;理论上的 7应用层&#xff1a;定义了应用进程间通信和交互的规则&#xff0c;常见协议有HTTP、SFTP、DNS、WebSocket6表示层&#xff1a;数据的表示、安全、压缩。确保一个系统的应用层所发消息能被另一个系统的应用层读取…...

科普文:一文搞懂jvm原理(四)运行时数据区

概叙 科普文&#xff1a;一文搞懂jvm(一)jvm概叙-CSDN博客 科普文&#xff1a;一文搞懂jvm原理(二)类加载器-CSDN博客 科普文&#xff1a;一文搞懂jvm原理(三)执行引擎-CSDN博客 前面我们介绍了jvm&#xff0c;jvm主要包括两个子系统和两个组件&#xff1a; Class loader(类…...

《昇思25天学习打卡营第5天|数据变换 Transforms》

文章目录 前言&#xff1a;今日所学&#xff1a;1. Common Transforms2. Vision Transforms3. Text Transforms 前言&#xff1a; 我们知道在进行神经网络训练的时候&#xff0c;通常要将原始数据进行一系列的数据预处理操作才会进行训练&#xff0c;所以MindSpore提供了不同类…...

详细分析Oracle修改默认的时间格式(四种方式)

目录 前言1. 会话级别2. 系统级别3. 环境配置4. 函数格式化5. 总结 前言 默认的日期和时间格式由参数NLS_DATE_FORMAT控制 如果需要修改默认的时间格式&#xff0c;可以通过修改会话级别或系统级别的参数来实现 1. 会话级别 在当前会话中设置日期格式&#xff0c;这只会影响…...

uniapp零基础入门Vue3组合式API语法版本开发咸虾米壁纸项目实战

嗨&#xff0c;大家好&#xff0c;我是爱搞知识的咸虾米。 今天给大家带来的是零基础入门uniapp&#xff0c;课程采用的是最新的Vue3组合式API版本&#xff0c;22年发布的uniappVue2版本获得了官方推荐&#xff0c;有很多同学等着我这个vue3版本的那&#xff0c;如果没有学过vu…...

C#面:现有一个整数number,请写一个方法判断这个整数是否是2的N次方

要判断一个整数是否是2的N次方&#xff0c;可以使用位运算来实现。一个整数如果是2的N次方&#xff0c;那么它的二进制表示中只有一位是1&#xff0c;其余位都是0。可以通过将这个整数与它减去1的结果进行按位与运算&#xff0c;如果结果为0&#xff0c;则说明这个整数是2的N次…...

智能与伦理:Kimi与学术道德的和谐共舞

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 Kimi&#xff0c;由月之暗面科技有限公司开发的智能助手&#xff0c;擅长中英文对话&#xff0c;能处理多种文档和网页内容。在论文写作中&#xff0c;Kimi可提供资料查询、信息整理、语…...

云端AI大模型群体智慧后台架构思考

1 大模型的调研 1.1 主流的大模型 openai-chatgpt 阿里巴巴-通义千问 一个专门响应人类指令的大模型。我是效率助手&#xff0c;也是点子生成机&#xff0c;我服务于人类&#xff0c;致力于让生活更美好。 百度-文心一言&#xff08;千帆大模型&#xff09; 文心一言"…...

electron教程(二)控制应用程序的事件生命周期

1.will-finish-launching 当应用程序完成基础的启动的时候被触发&#xff0c;在 Windows 和 Linux 中, will-finish-launching 事件与 ready 事件是相同的; 在 macOS 中&#xff0c;这个事件相当于 NSApplication 中的 applicationWillFinishLaunching 提示。 app.on(will-fi…...

MySQL的Geometry数据处理之WKB方案

MySQL的Geometry数据处理之WKT方案&#xff1a;https://blog.csdn.net/qq_42402854/article/details/140134357 MySQL的Geometry数据处理之WKT方案中&#xff0c;介绍WTK方案的优点&#xff0c;也感受到它的繁琐和缺陷。比如&#xff1a; 需要借助 ST_GeomFromText和 ST_AsTex…...

长安马自达:EZ-6只是开始,每年推出一款新产品

在重庆车展期间,笔者采访了长安马自达汽车有限公司执行副总裁邓智涛与MAZDA EZ-6设计师星野忠男,对其合资模式2.0概念,以及最新车型MAZDA EZ-6的市场定位与未来规划进行了深度解析。邓智涛首先回顾了中国合资车企40年的发展历程,并阐述了合资模式2.0的诞生背景。他指出,中…...

4JJ1动力+定制化冷厢翼放冷链版助你夏日创富遥遥领“鲜”

夏日高温,对于生鲜食材的运输来说是个巨大的挑战。如何确保物品新鲜及时的送达,成为摆在物流行业面前不得不思考的问题。冷链物流的重要性愈发凸显,而高效、可靠的冷藏车则成为了解决这一问题的关键。那么,面对市场上琳琅满目的冷藏车产品,如何选到一款既省心又高效赚钱的…...

深圳建设“超充之城”提速

五分钟的时间可以做什么?也许只是白领喝一杯咖啡的时间,但在深圳,能给新能源汽车续航200公里。在深圳的超充站,“一杯咖啡,满电出发”的标语十分醒目。深圳随处可见的超充标语近日,记者从深圳市发展改革委获悉,截至5月17日,深圳累计建成超充站378座。深圳“超充之城”建…...

非量表题如何进行信效度分析

效度是指设计的题确实在测量某个东西&#xff0c;一般问卷中使用到。如果是量表类的数据&#xff0c;其一般是用因子分析这种方法去验证效度水平&#xff0c;其可通过因子分析探究各测量量表的内部结构情况&#xff0c;分析因子分析得到的内部结构与自己预期的内部结构进行对比…...

基于广义极大极小凹惩罚的心电信号降噪方法(MATLAB R2021B)

凸优化是数学最优化的一个子领域&#xff0c;研究定义于凸集中的凸函数最小化问题。由于心电信号降噪的过程可以理解为求信号的稀疏近似解&#xff0c;因此基于凸优化和稀疏性表达的去噪方法可用于心电信号处理。在凸优化的数学模型中&#xff0c;惩罚项的选取对最终结果会产生…...

matplotlib ---词云图

词云图是一种直观的方式来展示文本数据&#xff0c;可以体现出一个文本中词频的使用情况&#xff0c;有利于文本分析&#xff0c;通过词频可以抓住一篇文章的重点 本文通过处理一篇关于分析影响洋流流向的文章&#xff0c;分析影响洋流流向的主要因素都有哪些 文本在文末结尾 …...