CIKM 2025 | 针对学习型布隆过滤器的通用自适应内存分配(附论文和源码)

存在性测试,即判断一个元素是否属于某个集合,在诸如数据库系统和网络应用等领域被广泛运用。布隆过滤器可以高效的完成该测试,但存在大数据集下内存开销大,假阳率过高等问题。学习型布隆过滤器通过将学习模型和布隆过滤器相结合,通过捕捉数据分布,缓解了上述困难。尽管现有研究针对部分参数的设计有了部分尝试,但如何协调学习模型和布隆过滤器的内存分配以进一步降低假阳率,仍面临许多挑战。本次为大家带来重庆大学时空实验室,北师香港浸会大学广东省数据科学与技术交叉应用重点实验室和京东智能城市研究院联手在CIKM 2025发表的文章《General Adaptive Memory Allocation for Learned Bloom Filters》。

图片

引言

存在性测试被广泛运用于许多领域:在数据库查询中,存在性测试可以通过提前过滤掉许多不存在的查询,来降低数据库查询的IO以及网络开销,起到加速查询的效果。在该场景的核心挑战是如何权衡前缀过滤器的内存开销以及对于查询的增益。因此,能完成近似的存在性测试的布隆过滤器作为在有限内存开销下的重要工具被提出。传统的布隆过滤器与数据分布无关,使用一系列哈希函数将元素映射至一个位数组。学习型布隆过滤器(LBF)将学习模型与布隆过滤器结合,通过捕捉数据分布,在更少的内存占用下完成对数据的存在性判定,以进一步降低在有限内存空间下布隆过滤器的假阳率。该数据结构通过前置的学习模型预测特定样本是否存在于集合中,并通过后置的布隆过滤器进一步确认这些不确定的预测。后续研究提出了许多各异的结构变动,以优化LBF的假阳率表现。然而,研究发现,针对所有LBF变体,在学习模型与布隆过滤器间获得一个最优的内存比例能显著降低整体的假阳率。如图一所示,在相同内存限制下,不同内存比例会带来约10倍的假阳率差异;对于不同的总内存,最佳模型内存比例也大不相同。因此,对于一个LBF,在给定的内存限制下找到最优的内存分配策略十分重要。

图片

内存分配重要性实验

获得该策略难点有三:第一,内存分配与LBF的整体表现间的关系复杂,如何确认内存分配孰优孰劣?第二,如何在分配的固定内存大小下训练一个学习模型?第三,内存分配的比例是连续的,且学习模型的相关参数也是连续的。简单枚举带来的构建时间开销本身与加速查询的初衷相悖。

为解决以上三个难点,我们针对LBF及其所有变体提出了第一个通用的可变内存分配框架Gama,在特定内存的限制下最小化其假阳率。针对挑战一,对于特定的内存大小以及数据集,Gama通过多轮探索可能的内存分配,选择出最优内存分配策略。针对挑战二,Gama先确定占用特定内存大小的学习模型及其相关参数,并将剩余的内存分配给布隆过滤器。为避免构建LBF的时间开销,我们提出了一种不需要构建就可以评估LBF假阳率低方案。针对挑战三,我们充分利用训练过程来改变学习模型的内存占用,从而避免列举无限和连续的内存分配策略和参数。并且,我们进一步将探索过程转换为一个优化问题,并使用贝叶斯优化进一步提升Gama的效率。具体的说,Gama使用两种内存分配策略:通过增量训练确保精确控制内存利用的基于循环的方案,和使用概率探索来优化内存分配,使其在大型内存预算下实现与基于循环方法接近的FPRs,同时显著提高效率的基于贝叶斯优化的方案。为更好的平衡效率和假阳率,我们将上述两种方案结合完成了一个混合式的方案。综上所述,本文的贡献点如下:

1)我们提出了第一个针对LBF的通用可调内存分配框架Gama。通过基于循环的方法在小内存开销下精确获得最优的内存分配策略,通过基于贝叶斯优化的方法在大内存开销下获得与基于循环的方案相近的假阳率。

2)我们构建了一个混合的方法,结合了两种方案的优势,在不同的内存开销下高效的优化了LBF的假阳率。

3)我们设计了一个方法来快速的确定LBF的最优参数及其对应的假阳率,避免了完整的构建LBF,降低了评估开销。

4)我们在三个真实世界的数据集上进行了广泛的实验,证明了Gama的有效性。在最好的情况下,Gama能将原始的LBF的假阳率下降69%

相关知识

2.1 背景知识

• 布隆过滤器:布隆过滤器用于确认一个元素x是否存在与一个集合S中。它包含一个固定大小的位数组和k个独立的哈希函数。初始化时,位数组中的所有元素被设为0。如图2所示,当一个元素被插入进入这个集合,布隆过滤器会使用这k个哈希函数将元素映射为位数组中的k位,并将他们设置为1。为确定元素x是否存在,布隆过滤器使用相同的k个哈希函数将待测元素x同样映射至位数组中,并检查对应的位数是否为1。如果是的话,那元素x有极高的可能性存在与集合S中。如图2中的“COVID-19”,若元素真的存在与集合中,且布隆过滤器也觉得其存在,该元素被称为真阳性。如果映射的k位中有至少一位为0,则元素x一定不存在与集合中,如图2Epidemic”,则将该元素称为真阴性。由于哈希函数存在哈希碰撞的可能性,当所有对应的位是1,元素x仍可能不存在与集合S中,如图2中“Virus”所示,则该元素被称为假阳性。布隆过滤器不存在任何假阴性样本。假设位数组的大小为μB,哈希函数个数为k,需要插入布隆过滤器的元素个数为nB,则布隆过滤器的假阳率FB可以由如下式子计算得到:

图片

当采用准最优的k = μB*ln 2/nB 时,可以进一步得到布隆过滤器的假阳率:

图片

布隆过滤器结构图

• 学习型布隆过滤器:当数据集很大时,布隆过滤器会占用很大的内存且其假阳率会很高。因此,学者将学习模型和布隆过滤器结合,提出了学习型布隆过滤器(LBF)。如图3所示,当使用一些可用的训练数据进行提前训练时,学习模型可以依据数据的特征,判断元素x是否属于集合S。具体而言,LBF先设置阈值τ,随后将元素x放入学习模型中,获得预测分数 f(x)。当f(x)>τ时,元素x被认为属于集合S,反之,x会被送入布隆过滤器进行进一步的确定。LBF的假阳率FL可以由下式计算得出:FL=FM +(1-FM)*FB其中FM 表示学习模型的假阳率,FB 代表布隆过滤器的假阳率。该方程表示,对于一个负样本,有两种方式可以使其被错误地分类为一个正样本。第一,学习模型可能会以FM的概率错误地将其预测为阳性。第二,当模型将其预测为阴性样本时,布隆过滤器有1-FMFB的概率会错误的将其判定为存在。构建一个好的LBF需要在学习模型和布隆过滤器间权衡。若学习模型足够复杂,它的预测准确率会更高,并占用更多的内存,使得分配给布隆过滤器的内存变少。因此布隆过滤器的假阳率将变大。为在上述权衡中得到一个最优的内存分配策略,我们提出了Gama

图片

学习型布隆过滤器结构图

2.2 问题定义

给定一个内存预算μLBF 上的内存分配问题旨在找到学习模型内存μM* BF 内存μB的最佳组合,使得 LBF 的整体 FPR最小化,即:

图片

基于循环的内存分配策略

我们需要确定模型结构和阈值以构建一个LBF。在本节中,我们提出了基本基于循环的内存分配方法,包括α个轮次,如图4所示。在每轮次中,有两个主要步骤,即模型训练和阈值选择。在模型训练步骤中,我们构建一个学习模型并对其进行训练以适应训练数据集。在阈值选择步骤中,我们选择本轮次中具有最小FPR的最优阈值,形成局部最佳学习模型。经过α个轮次后,我们从所有局部最佳学习模型中选择全局最佳学习模型。

图片

基于循环的内存分配框架

3.1 模型训练

模型训练涉及两个主要任务:训练一个能够高效有效地执行二分类的模型,以及在训练过程中调整学习模型的内存比例。在这一步,我们关注学习模型的选择以及如何在不同的训练周期之间分配内存。

• 模型选择:一个运用于LBF中的良好学习模型应满足以下要求。首先,模型应足够轻量,即占用很少的内存。其次,学习模型的准确性应尽可能高,以用于二分类任务。第三,随着轮次的增加,模型应自行扩展以避免从头开始重新训练。

作为一种轻量级的梯度提升决策树模型,LightGBM可以满足所有三个要求。首先,LightGBM应用了直方图算法,将连续特征值分箱到离散的箱中,以减少其内存消耗。其次,LightGBM的结构确保了其良好的分类任务处理能力。第三,其梯度提升算法可以通过在针对残差训练时扩大自身,使其可以在不重新训练模型的情况下利用上一个轮次的结果继续训练。因此,我们选择LightGBM作为我们的默认模型。

• 轮次间的内存变化:接下来,我们介绍如何调整LightGBM在各个轮次之间的内存消耗。在每个迭代中,LightGBM训练一个新的决策树并将其插入到模型中。更确切地说,在训练过程中,LightGBM根据最大树深度的限制来分割它找到的最佳节点。节点和树的数量不断增加使得模型的大小变大,选择减少损失函数的叶节点可以确保我们得到最佳的LightGBM模型。因此,随着迭代的进行,学习模型的大小逐渐增大,我们可以列举所有可能的内存分配。

3.2 阈值选择

在轮次i中,在进行了模型训练步骤后,我们获得了一个能拟合数据集的学习模型Modeli。在阈值选择步骤中,我们需要找到一个最优阈值,将学习模型和布隆过滤器连接起来,确定哪些项目需要插入到布隆过滤器中。为实现这一点,一个直观的想法是将预测分数空间均匀地划分为几个区域,然后从中选择一个阈值,以获得最佳性能。然而,我们发现这种方法性能较差。这是因为预测分数的分布十分不均。从均匀划分的区域中选择一个阈值会导致我们损失密集区域中很多备选阈值。

因此我们使用如下方案进行阈值选择。对于数据集中的每一个元素j,模型会生成一个可以被当作阈值的预测得分τji,我们从所有预测得分中选择一个最好的阈值τi来达成LBF的最小FPR。其中n代表训练数据集中的元素总数。对于每个阈值τji,我们需要计算LBF对应的假阳率。直观的方法是将数据集中的所有项目插入到LBF中,然后测量FPR是多少。然而,这种方法可能有两个缺点。首先,任何被学习模型判断为不存在的元素都需要被插入到布隆过滤器中,这是一个非常耗时的步骤。其次,可用阈值为n个,对于每个可用阈值,我们可能最多需要将n个与元素插入到布隆过滤器中,导致时间复杂度为O(n^2)

为进一步加速假阳率的计算,我们采取了以下两部分优化。优化一:我们不直接将元素插入布隆过滤器以计算假阳率。根据第二章提到的LBF假阳率计算公式,在给定的轮次i中,对于特定的阈值τji,我们只需要知道由学习模型造成的假阳率FMi,以及由布隆过滤器造成的假阳率FBi,便可计算LBF整体的假阳率FL,jiFMi 可以由nMi除以nneg计算得出。其中,nMi代表被学习模型错误判断为存在的负样本个数,nneg代表在训练数据集中的总负样本的数量。同样根据第二章提到的布隆过滤器的假阳率计算公式,我们可以轻松计算布隆过滤器的假阳率FBi。根据Gama的内存分配逻辑,分配给布隆过滤器的内存大小μBi为总内存开销μ减学习模型占用的内存大小Size(Modeli)。优化二,我们提出一种基于排序的方法来高效计算nMinBi

首先,我们按预测分数将训练数据集中的项目升序排序。然后,我们从大到小遍历可用阈值。此时,只需检查当前阈值对应的元素是否真实存在于集合中,便可增减nMinBi

3.3 伪代码

算法1展示了基于循环的分配方案的伪代码。第13行我们通过构建一个经典的布隆过滤器来完成初始化。并在后续的环节中,使用μB*μM*来记录目前为止最优的布隆过滤器和学习模型的内存大小,使用FL*记录。第56行为每个轮次中的模型训练步骤,第715行则是每个轮次中的阈值选择步骤。在模型训练中,如果学习模型占用的内存超过了总内存限制μ,我们会直接跳出循环并返回最终结果。

假设轮次数为。由于在每一轮中我们都需要对预测得分进行排序,所以算法1的时间复杂度为O(α*n*logn)

图片

算法基于循环的内存分配方案的伪代码

基于贝叶斯优化的内存分配策略

4.1 研究动机

基于循环的内存分配方法可以在内存预算μ下有效地找到最优分配策略。然而,如果μ足够大,可能会需要过多的轮次,花费太多时间来获得一个好的内存分配策略。而且,LBF的构建效率对于某些关键场景至关重要,例如,在数据库系统中,如果数据集被更新,我们需要高效地重建LBF以保证查询结果的正确性和系统的可用性。

LBFs的内存分配问题也可以被视为参数调优问题。因此,我们可以利用参数调优方法来解决这个问题。给定一个内存预算μ,我们尝试找到一个比例η=μM*/μ,用于构建一个能使LBF整体假阳率最小的学习模型,其中η[0,1]是一个连续变量。然而,训练一个恰好占用给定内存量的学习模型具有挑战性。为此,将寻找一个最好的比例η,转换为找到一个在算法1中提到的最优的迭代次数

图片

基于贝叶斯优化的内存分配框架

4.2 直观方法

贝叶斯优化被广泛应用于参数调优问题中。与基于强化学习和深度学习的方法相比,它仅需更少的样本便能高效地获得高质量的参数配置,这使其特别适用于参数数量较少的场景。在我们的问题中,有且只有一个参数(即α)需要调优。此外,我们的问题还要求快速的预测速度和较小的内存占用。因此,我们采用贝叶斯优化作为我们的参数调优方法。

5a)展示了迭代式的直观方法框架图。首先,贝叶斯优化器预测一个合适的轮次αj。然后,我们使用基于循环的方法,设定轮次数为αj,获得一个全局最小的假阳率FL,j*并将元组(αjFL,j*)加入观察集。在迭代轮数小于设定的最大迭代轮数NBO时,重复以上两个步骤。最后,返回观察集中的最优轮次数α*,以及对应的最小的假阳率FL*

4.3 本文方法

直观方法有两个不足之处。首先,它需要针对预测出的每一个αj,都调用基于循环的内存分配方法从零开始执行αj个轮次,这个过程依然相当耗时。其次,在每个周期内部,都必须进行涉及大量预测运算以及排序的阈值选择,带来了极大的计算开销。

为解决上述问题,我们提出了一个优秀的基于贝叶斯优化的内存分配方案,如图5b)所示。相比直观方案,我们完成了三点主要的优化。

• 优化一:我们充分利用了历史训练生成的模型结构。由于我们选用 LightGBM 作为默认学习模型,其特点在于新模型Modeli+1是在旧模型Modeli的基础上直接添加一棵决策树生成的,原有的i棵决策树会保持不变。基于这一特性,我们缓存了用历史最大轮数αmax训练出的模型Modelαmax。假设在新一轮迭代中,贝叶斯优化器建议的轮数是αj。若αj小于αmax,我们不必重新训练,只需从缓存的Modelαmax中删掉末尾的 (αmaxαj棵决策树,就能得到需要的学习模型(如图5(b) 中红色虚线箭头所示)。若αj 大于αmax,我们则从Modelαmax出发,继续训练 (αjαmax轮得到新的学习模型,并用它来更新缓存的Modelαmax(如图5(b) 中蓝色点状箭头所示)。

• 优化二:我们在特定的轮次会跳过特定的部分操作。如图5(b),若αj 小于αmax,我们仅在轮次αj 时进行阈值选择;αj大于αmax,我们只进行轮次等于(αmax+1)和(αj -1)的两次模型训练步骤。

• 优化三:我们将观察集中的观察值替换为了(αjFLαj),其中FLαj代表LBF在学习模型训练轮次为αj时的假阳率。这是因为当我们应用了上述两个优化时,我们没法得到全局最小的假阳率。实验部分也同样验证了我们的优化可以让我们得到最优的内存分配策略。

• 伪代码:算法2给出了基于贝叶斯优化的内存分配方法的伪代码。它的第1-3行与算法1的初始化过程类似,但额外增加了对历史最大轮数αmax和观测集OB的初始化。第4-22行是贝叶斯优化器的核心迭代过程,其逻辑和图5(b)所描绘的流程基本一致。值得一提的是,我们并未考虑αj  =αmax的特殊情况,因为贝叶斯优化器能够依据已有的观测集主动规避这一情形。在整个流程中,模型训练最多需要进行αmax次,而阈值选择最多执行NBO次。因此,算法2的时间复杂度为O(αmaxNBO *n*logn),这里的n代表训练数据集中的样本总数。在空间复杂度方面,由于算法2只是使用缓存已训练最大轮次的学习模型替换了算法1中进入下一轮次训练的学习模型,

算法2的空间复杂度与算法1是相同的,均为 O(n)

图片

算法基于贝叶斯优化的内存分配算法的伪代码

讨论

5.1 两种内存分配方案的结合

当内存预算充足时,基于贝叶斯优化的方法因能省去大量阈值选择开销而效率很高。但反过来,如果内存预算太小,其自身(由贝叶斯优化器)带来的额外计算开销反而会超过节省的成本,导致性能还不如基于循环的方法。针对这个问题,我们提出了一种混合方法,旨在结合上述两种方法的长处。我们的设计思路是:当内存预算非常有限时,基于循环的方法其实只需很少几轮(如不超过5轮)就能找到足够好的内存分配方案,此时根本没必要动用贝叶斯优化器。此外,这种混合方法还有一个好处:我们可以用基于循环的方法得到的结果来初始化观测集和缓存模型,从而为后续可能启用的贝叶斯优化器提供一个更好的起点。

• 伪代码:算法3给出了混合的内存分配方法的伪代码。在我们的论文中,我们将参数NLB的值设定为5。因为NLB的取值通常很小,这意味着由基于循环的方法引入的开销基本可以忽略不计,所以算法3的时间复杂度和空间复杂度与算法2是相同的。

图片

算法混合的内存分配算法的伪代码

实验与评估

6.1 实验设置

• 数据集:为了评估Gama 框架的性能,我们采用了三个现实世界的数据集,分别是:天体数据集 (COD)Yelp URL。详细来说,COD 数据集的数据来源于斯隆数字化巡天项目 (SDSS),它根据天体的光学与光谱属性对天体进行分类。Yelp 数据集主要由用户对商家的评论构成,可用于分析消费者的反馈与情感倾向。而 URL 数据集则由恶意与良性两种URL组成。值得一提的是,为了缩短编码长度,我们对所用的Yelp数据集进行了过滤,去除了其中数据极其稀疏的部分。在所有评估中,若无特别说明,均使用URL数据集。各个数据集的样本数量参见表1

数据集的相关信息

图片

• 评估指标:我们使用两个指标来评估性能:假阳率和构建时间。假阳率衡量LBF的性能。另一方面,构建时间则通过构建数据结构所需的时间来评估模型的效率。

• 实验设置:默认情况下,我们使用 LightGBM 作为学习模型,且 Gama 使用基于混合的内存分配。对比方法的学习模型是通过训练过程得到的最优二分类模型。默认的学习模型大小根据相关文献的规定,被设置为39KB。总内存预算的配置范围从64KB320KB,增量为64KB。剩余的内存被分配给布隆过滤器。实验在配备了4CPU128GB内存和4T硬盘的服务器上进行。所有方法均使用Python 3.10版本实现。所有源代码均已发布于https://github.com/Spatio-Temporal-Lab/LBF-Gama。关于相关数据集嵌入的综合详情,请参阅README文档。

我们在LBF及其两种变体Sandwich LBF(简称SLBF)  PLBF 上运行Gama。我们将由Gama优化的LBF称为G-LBF,由Gama优化的SLBF称为G-SLBF,由Gama优化的PLBF称为G-PLBF。尽管Ada-BF是一种先进的LBF变体,但其高昂得令人望而却步的构建时间(例如,在COD数据集上接近20小时)使其在实际应用中并不可行。考虑到其与PLBF的结构相似性,我们选择了PLBF作为我们改进的基础。在实验构建方面,原始的LBF变体是使用开源代码库实现的,并且参数也设置为开源库中的默认值。

6.2 整体对比

为验证Gama的有效性,我们比较了不同LBF变体在Gama优化前后、不同内存预算下的FPR。表2和表3分别展示了LBFG-LBFFPR及构建时间对比,从中我们可以发现以下结果:(1) Gama在所有情况下都有助于LBF降低其FPR(2) Gama在绝大多数实验场景中实现了FPR的指数级降低。(3) Gama为找到最优内存分配策略需要一些额外的构建时间。然而,其构建时间以线性速率增长,与FPR的指数级降低相比,这是一个可接受的权衡。考虑到降低的FPR显著减少了索引结构的运行时开销,这一点尤其合理。

假阳率对比

图片

构建时间对比

图片

6.3 不同LBF变体上的表现

为了检验Gama框架对于不同LBF变体的通用性,我们选取了两种经典的LBF变体进行了实验。其中,SLBF的思路是在LBF前额外加一个布隆过滤器来过滤掉多数负样本;而PLBF则是将分数空间切分成几个区域,为每个区域配置不同假阳率的布隆过滤器。在本次实验中,我们统一将内存预算设为192KB。图6的实验结果表明:1Gama能够帮助所有LBF变体有效降低FPR2)我们在原始LBF上发现的用构建时间换取更低FPR”这一权衡关系,在这些变体上依然适用。3)通常情况下,G-PLBFFPR要低于G-SLBF,但其构建时间也相应更长。总的来说,这个实验证明了Gama框架对于结构各异的LBF变体同样具有良好的稳定性。

图片

不同LBF变体上的表现

6.4不同学习模型的表现

为了展示Gama广泛的适用性,我们固定了200KB的内存预算,并测试了它搭配随机森林和人工神经网络这两种不同学习模型时的性能。具体调优方式是:对于ANNGama通过增减神经元数量来调整内存分配;对于随机森林,则通过增减决策树的数量。从图7的结果可以看出:(1) 内存分配策略至关重要。无论底层用的是哪种学习模型,LBF的假阳率都对分配给模型的内存比例极为敏感。(2)Gama能够为不同的学习模型都找到近似最优的配置,从而获得接近最低的假阳率,这体现了其广泛的适用性和有效性。

图片

不同学习模型的表现

6.5 参数对Gama的影响

如算法2所述,贝叶斯优化的迭代次数NBO,在贝叶斯优化过程本身的性能中扮演着至关重要的角色。图8展示了Gama在不同数据集、不同NBO取值下的性能。随着NBO的增加,假阳率呈现出轻微的下降趋势,但总体上保持相对稳定,而构建时间则略有增加。稳定的FPR表明,尽管相关超参数发生变化,Gama仍能保持优秀的性能。

图片

不同NBO下的表现

6.6 消融实验

我们进行了一项消融研究,以证明Gama有效地结合了基于循环的方法和基于贝叶斯优化的方法,在显著降低构建时间的同时,实现了优秀的假阳率。我们评估中使用的固定0.1分配比例,是基于在64KB预算下对0.10.9范围进行扫描的初步测试后,根据经验确定的最佳平衡点。图9展示了实验结果,其中 LBF+Loop 表示使用基于循环的方法,而 LBF+Bayes 表示使用基于贝叶斯优化的方法。如图9(a)所示,随着总内存预算的增加,所有方法的假阳率都会下降。然而,Gama在所有内存预算下都持续获得了最低的FPR值。关于构建时间,如图9(b)所示,所有方法的曲线都随着总内存的增加呈现出增长趋势。Gama在所有内存开销下都实现了低假阳率并保持了低构建时间,展示了其高效性和鲁棒性。

图片

消融实验

总结

我们为LBF提出了一种有效的通用自适应内存分配框架Gama。具体而言,Gama包含两种有效的内存分配策略:基于循环的方法确保了精准的内存分配,且非常适用于内存约束严格的场景;而基于贝叶斯优化的方法则擅长在较大内存预算下高效地探索配置。基于这两种方法,我们进一步引入了一种结合了两者优点的混合方法,它在几乎所有的内存约束下都表现良好。在三个大规模数据集上进行的大量实验验证了我们框架Gama的有效性,在最佳情况下实现了69%的假阳率降低。

-End-

本文作者
尚游
重庆大学计算机科学与技术专业2022级本科生,重庆大学Start Lab团队成员。
主要研究方向:时空数据挖掘,智能索引
图片


重庆大学时空实验室(Spatio-Temporal Art Lab,简称Start Lab),旨在发挥企业和高校的优势,深入探索时空数据收集、存储、管理、挖掘、可视化相关技术,并积极推进学术成果在产业界的落地!年度有3~5名研究生名额,欢迎计算机、GIS等相关专业的学生报考!
       Image


              图文|尚   游

              校稿|李   政

              编辑|李佳俊

              审核|李瑞远

              审核|杨广超

关注公众号,回复“Gama”获取论文及源码

0 条评论

    发表评论

    电子邮件地址不会被公开。 必填项已用 * 标注