LSH算法:高效相似性搜索的原理与Python实现II

image.png

 局部敏感哈希(LSH)是一种高效的近似相似性搜索技术,广泛应用于需要处理大规模数据集的场景。在当今数据驱动的世界中,高效的相似性搜索算法对于维持业务运营至关重要,它们是许多顶尖公司技术堆栈的核心。

相似性搜索面临的主要挑战在于处理庞大的数据规模。许多企业每天都要处理从百万到数十亿不等的数据点。例如,面对一亿个数据点,逐个进行比较显然是不切实际的。

更进一步,企业的需求远不止单次搜索。以谷歌为例,它每分钟处理的搜索请求超过380万次。这种高频率的搜索需求,再加上数据点的规模,构成了一个巨大的技术挑战。

此外,还没有涉及到数据的维度问题或相似性函数的复杂性。在这些因素的共同作用下,对于大型数据集进行全面的搜索变得不可行。

那么,如何在如此难以想象的大规模数据集上进行有效搜索呢?答案就是近似搜索。通过近似搜索,不必对每一对数据点进行详尽的比较。相反,采用LSH技术,它通过近似方法筛选出潜在的匹配项,从而大幅减少了需要进行详尽比较的数据点数量。

通过这种方式,LSH不仅提高了搜索效率,还保持了搜索结果的准确性,使其成为大规模数据集相似性搜索的理想解决方案。

局部敏感哈希(LSH)

局部敏感哈希(LSH)是一种广泛使用的近似最近邻搜索(ANNS)方法。它依赖于一种特殊的哈希函数,这种函数设计用来将相似的项目映射到同一个哈希桶中。面对大规模数据集,LSH通过哈希函数将项目分配到不同的桶,从而简化搜索过程。

LSH算法的一个关键特点是它与常规哈希函数不同。传统哈希函数致力于最小化哈希冲突,而LSH算法则有意增加哈希冲突的概率,目的是将相似的项目聚集在一起。

image.png

哈希函数对比:上图展示了两种哈希函数的效果。顶部(蓝色)的哈希函数致力于最小化哈希冲突,而底部(品红色)的哈希函数则是LSH使用的,它旨在最大化相似项之间的哈希冲突。

在LSH中,相似的向量倾向于产生相同的哈希值,并因此被分到同一个桶里。相对地,不相似的向量则希望不会被分到同一个桶中。

使用LSH进行搜索

LSH搜索过程包括以下三个步骤:

  1. 索引向量:首先,将所有向量通过LSH哈希函数处理,并将它们索引到对应的哈希桶中。
  2. 哈希查询向量:当引入一个查询向量时,使用相同的LSH哈希函数对其进行处理。
  3. 桶比较:然后,通过比较汉明距离来识别查询向量与哪些哈希桶中的向量最近。

这些步骤构成了LSH方法论的基础,将在后续的文章中对这些概念进行更深入的探讨和详细说明。

近似效果

在深入研究LSH技术之前,重要的是要认识到,通过将向量映射到低分辨率的哈希向量中,实际上是在进行一种近似处理。这种方法意味着搜索过程可能不会详尽无遗地比较每个向量,因此预期搜索的精确度会有所降低。

image.png

将可能非常大的密集向量压缩成高度压缩的二进制向量,以实现更快的搜索速度。虽然这种压缩牺牲了一定的搜索质量,但它显著提高了搜索效率。

方法选择

LSH有多种实现方式,每种方法使用不同的哈希构建技术和距离或相似度度量。在这里不深入细节,因为不同的版本适用于不同的应用场景。

最受欢迎的两种LSH实现方法是:

  • 文档分片、MinHashing和带状LSH:这是一种较为传统的LSH方法,适用于特定类型的数据集和查询。
  • 随机超平面与点积和汉明距离:这种方法使用随机超平面来构建哈希函数,并通过点积和汉明距离来衡量向量间的相似性。

本文将专注于介绍随机超平面方法,它不仅更常用,而且在多个流行库中得到了实现,例如Faiss。这种方法因其高效性和易于实现的特点,在工业界和学术界都受到了广泛的关注。

随机超平面(Random Hyperplanes)

随机超平面方法,尽管听起来简单,实际上是一种高效的技术,用于在高维空间中进行近似最近邻搜索。这种方法可能难以理解,但通过以下示例,将深入探讨其工作原理。

这里使用Sift1M数据集进行示例。假设我们有一个查询向量xq,目标是从数组xb中识别出前k个最近邻。

image.png

返回查询向量xq的三个最近邻

创建超平面

在随机超平面方法中,通过构建超平面来分割数据点。每个超平面由一个法向量定义,数据点根据与法向量的点积结果被分配为0或1。

image.png

位于超平面正侧的数据点分配值1,为负侧的数据点分配值0

确定数据点位于超平面哪一侧的关键在于超平面的法向量。点积的结果告诉数据点位于超平面的哪一侧。如果两个向量方向相同,点积结果为正。如果它们方向不同,结果为负。

image.png

当超平面法向量与另一个向量产生 +ve 点积时,可以将该向量视为位于超平面前面。对于产生 -ve 点积的向量来说,情况正好相反

在两个向量完全垂直(位于超平面边缘)的极少数情况下,点积为0——将这种情况归入负方向向量。

单个二进制值并不能告诉太多关于向量相似性的信息,但当添加更多超平面时,编码的信息量会迅速增加。

image.png

添加更多超平面来增加二进制向量中存储的位置信息量。

通过使用这些超平面将向量投影到低维空间中,产生新的哈希向量。

在上图中,使用了两个超平面,实际上可能需要更多——这个特性通过nbits参数定义。如果使用四个超平面,通过将nbits设置为4。

在Python中创建超平面的法向量。

 

python

复制代码

nbits = 4 # 超平面的数量 d = 2 # 向量的维度 import numpy as np # 创建一组随机法向量 plane_norms = np.random.rand(nbits, d) - .5 plane_norms

 

lua

复制代码

array([[-0.26623211, 0.34055181], [ 0.3388499 , -0.33368453], [ 0.34768572, -0.37184437], [-0.11170635, -0.0242341 ]])

通过 np.random.rand 创建了一组 0 → 1 范围内的随机值。然后添加-.5使数组值以原点 (0, 0) 为中心。可视化这些向量:

image.png

定义超平面位置的法向量,均以原点 (0, 0) 为中心

哈希向量

给定几个向量,可以使用这些法向量来计算它们的哈希值。

 

python

复制代码

a = np.asarray([1, 2]) b = np.asarray([2, 1]) c = np.asarray([3, 1]) # 计算点积 a_dot = np.dot(a, plane_norms.T) b_dot = np.dot(b, plane_norms.T) c_dot = np.dot(c, plane_norms.T) a_dot # array([ 0.41487151, -0.32851916, -0.39600301, -0.16017455]) a_dot = a_dot > 0 b_dot = b_dot > 0 c_dot = c_dot > 0 a_dot # array([ True, False, False, False]) # 将点积结果转换为二进制值 a_dot = a_dot.astype(int) b_dot = b_dot.astype(int) c_dot = c_dot.astype(int) a_dot # array([1, 0, 0, 0]) b_dot # array([0, 1, 1, 0]) c_dot # array([0, 1, 1, 0])

再次可视化,得到了三个向量 a、b 和 c,以及四个超平面(垂直于它们各自的法向量)。分别取 +ve 和 -ve 点积值得出:

image.png

0表示矢量位于平面后面(-ve 点积),1表示矢量位于平面前面(+ve 点积),组合起来创建二进制向量

LSH使用哈希向量来创建桶,每个桶包含具有相同哈希值的向量。

 

python

复制代码

vectors = [a_dot, b_dot, c_dot] buckets = {} i = 0 for i in range(len(vectors)): hash_str = ''.join(vectors[i].astype(str)) if hash_str not in buckets.keys(): buckets[hash_str] = [] buckets[hash_str].append(i) print(buckets) # {'1000': [0], '0110': [1, 2]}

现在搜索过程中,使用查询向量0111的哈希值来快速定位到相关的桶,然后通过比较汉明距离来找到最近的匹配。

使用这个向量,与LSH索引中的每个桶进行比较——在这个例子中只有两个值——1000和0110。使用汉明距离找到最近的匹配,这实际上是0110。

image.png

汉明距离,第一个两个向量之间有四个不匹配,汉明距离为4,接下来的两个只包含一个不匹配,汉明距离为1

使用LSH进行近似搜索意味着可能会牺牲一些搜索质量,但这是换取速度的代价。通过分组到桶中,显著减少了搜索所需的计算量。

平衡质量与速度

在相似性搜索中,一个关键的挑战是在搜索质量和速度之间找到合适的平衡点。

质量与速度的平衡

以我们的小规模示例为起点,注意到随机投影可能导致一些向量难以区分,例如,三个向量中的两个被映射到了相同的哈希值。现在,设想将这种情况放大到一个包含一百万个向量的大型数据集。

当引入查询向量xq并计算其哈希值(例如0111)时,发现它与两个桶(10000110)的汉明距离非常短。这种方法的速度非常快,因为它只需要两次距离计算就完成了搜索,但准确性却大大降低,因为可能返回了大约70万个哈希值为0110的样本。

桶的数量

在现实中,如果使用nbits值为4,将得到16个可能的桶:

 

python

复制代码

nbits = 4 # 计算nbits值的二进制组合数 1 << nbits # 16 # 打印给定nbits值的所有可能桶 for i in range(1 << nbits): # 获取整数的二进制表示,并格式化为nbits位 b = bin(i)[2:] b = '0' * (nbits - len(b)) + b print(b, end=' | ')

 

yaml

复制代码

0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |

即使有16个桶,一百万个向量被分成的桶数量仍然很少,导致每个桶内的样本非常不精确。

提高分辨率

为了提高搜索质量,可以通过增加超平面的数量来增加分辨率。更多的超平面意味着更高的分辨率二进制向量,从而产生更精确的向量表示。 通过nbits值来控制这种分辨率。一个更高的nbits值通过增加哈希向量的分辨率来提高搜索质量,但同时也可能增加搜索的计算成本。

image.png

增加nbits参数会增加用于构建二进制向量表示的超平面的数量

 

python

复制代码

for nbits in [2, 4, 8, 16]: print(f"nbits: {nbits}, buckets: {1 << nbits}")

 

yaml

复制代码

nbits: 2, buckets: 4 nbits: 4, buckets: 16 nbits: 8, buckets: 256 nbits: 16, buckets: 65536

通过调整nbits值,可以在搜索质量和速度之间进行权衡。在实际应用中,选择合适的nbits值是实现高效相似性搜索的关键。

Faiss中的LSH

回顾Faiss

Faiss(Facebook AI Similarity Search)是一个开源框架,专门用于高效实现相似性搜索。它提供了多种索引类型,包括IndexLSH,这是之前讨论过的局部敏感哈希(LSH)的高效实现。

初始化LSH索引

在Faiss中初始化LSH索引并添加数据集的示例代码如下:

 

python

复制代码

import faiss d = wb.shape[1] # 向量维度 nbits = 4 # 控制哈希向量的分辨率 # 初始化LSH索引 index = faiss.IndexLSH(d, nbits) # 添加数据集 index.add(wb)

执行搜索

一旦索引准备好,可以使用index.search(xq, k)方法进行搜索,其中xq是查询向量,k是希望返回的最近匹配数量。

 

python

复制代码

xq0 = xq[0].reshape(1, d) # 查询向量 # 搜索k个最近邻 D, I = index.search(xq0, k=10) # 返回最近邻的索引和距离 print("Indexes:", I) # 索引位置 print("Distances:", D) # 距离

测量性能

使用返回的索引,可以从数据集中检索原始向量,并计算它们与查询向量之间的余弦相似度。

 

python

复制代码

# 检索原始向量 retrieved_vectors = wb[I[0]] # 计算余弦相似度 cosine_sim = cosine_similarity(retrieved_vectors, xq0) print("Cosine Similarity:", cosine_sim)

 

ini

复制代码

array([[0.7344476 ], [0.6316513 ], [0.6995599 ], [0.20448919], [0.3054034 ], [0.25432232], [0.30497947], [0.341374 ], [0.6914262 ], [0.26704744]], dtype=float32)

从那些原始向量中,可以看到LSH索引是否返回了相关结果。通过测量查询向量xq0与前k个匹配之间的余弦相似性来进行这一操作。这个索引中有向量应该返回大约0.8的相似度分数,但返回的向量相似度分数仅为0.2,反映出性能低下。

诊断性能问题

如果返回的相似度分数较低,需要诊断性能问题。nbits值决定了索引中潜在桶的数量。如果nbits设置得太低,可能导致大量向量被分配到少数几个桶中,从而降低搜索质量。 如果尝试将1M个向量塞进只有16个哈希桶中,每个桶很可能包含10-100K+个向量。 所以,当哈希搜索查询时,它完美地匹配了这16个桶中的一个——但是索引无法区分被塞进那个单个桶中的大量向量——它们都有相同的哈希向量。可以通过检查距离D来确认这一点:

 

python

复制代码

print(D) # array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)

返回每个项目的完美距离分数为零,汉明距离只有在完美匹配时才能为零——这意味着所有的这些哈希向量必须是相同的。 如果所有的这些向量都返回完美匹配,它们必须都有相同的哈希值。因此,上述生成的索引无法区分它们——就LSH索引而言,它们都共享相同的位置。 如果增加k直到返回一个非零的距离值,应该能够推断出有多少个向量被分桶了这个相同的哈希码。

 

python

复制代码

k = 100 xq0 = xq[0].reshape(1, d) while True: D, I = index.search(xq0, k=k) if D.any() != 0: print(k) break k += 100 print(D) print(D[:, 172039:172041])

 

lua

复制代码

172100 array([[0., 0., 0., ..., 1., 1., 1.]], dtype=float32) array([[0., 1.]], dtype=float32)

一个包含172,039个向量的单个桶,这意味着是在从这172K个向量中随机选择前k个值。显然,需要减少桶的大小。 对于1M个样本,哪个nbits值有足够的桶以实现向量的更稀疏分布,通过计算平均值进行估计:

 

python

复制代码

for nbits in [2, 4, 8, 16, 24, 32]: buckets = 1 << nbits print(f"nbits == {nbits}") print(f"{wb.shape[0]} / {buckets} = {wb.shape[0]/buckets}")

 

ini

复制代码

nbits == 2 1000000 / 4 = 250000.0 nbits == 4 1000000 / 16 = 62500.0 nbits == 8 1000000 / 256 = 3906.25 nbits == 16 1000000 / 65536 = 15.2587890625 nbits == 24 1000000 / 16777216 = 0.059604644775390625 nbits == 32 1000000 / 4294967296 = 0.00023283064365386963

使用nbits值为16时,仍然在每个桶中得到大约15.25个向量——这看起来比实际情况要好。必须考虑到,有些桶会比其他桶大得多,因为不同的区域会包含更多的向量。 实际上,nbits值为24和32可能是实现真正有效桶大小的转折点。

 

python

复制代码

xq0 = xq[0].reshape(1, d) k = 100 for nbits in [2, 4, 8, 16, 24, 32]: index = faiss.IndexLSH(d, nbits) index.add(wb) D, I = index.search(xq0, k=k) cos = cosine_similarity(wb[I[0]], xq0) print(np.mean(cos))

 

复制代码

0.5424448 0.560827 0.6372647 0.6676912 0.7162514 0.7048228

看起来估计是正确的——前100个向量的整体相似度在每个nbits值增加之前突然上升,在𝑛𝑏𝑖𝑡𝑠==24nbits==24点之前稳定下来。

image.png

随着nbits值增加向量分辨率,结果将变得更加精确——可以看到更大的nbits值导致结果中余弦相似度更高。

提取二进制向量

Faiss允许提取向量的二进制表示,这有助于直接分析桶中的向量分布。

 

python

复制代码

# 提取二进制代码 arr = faiss.vector_to_array(index.codes) arr # array([ 5, 12, 5, ..., 15, 13, 12], dtype=uint8) arr.shape # (1000000,) # 将整数转换为二进制向量格式 (((arr[:, None] & (1 << np.arange(nbits)))) > 0).astype(int)

 

css

复制代码

array([[1, 0, 1, 0], [0, 0, 1, 1], [1, 0, 1, 0], ..., [1, 1, 1, 1], [1, 0, 1, 1], [0, 0, 1, 1]])

通过这个,可以可视化这些16个桶中向量的分布——显示使用最多的桶和一些空的桶。

image.png

当nbits == 4时,不同桶中向量的分布

通过调整nbits值,可以在搜索质量和速度之间找到平衡。在Faiss中使用LSH时,理解不同参数如何影响性能对于优化搜索结果至关重要。

使用LSH

局部敏感哈希(LSH)提供了一种快速的索引机制,尽管它可能不如平面(Flat)索引准确。在Sift1M数据集上,通过逐渐增加数据集的大小,发现在nbits值设定为768时,可以实现最佳的召回率——尽管这需要牺牲一些搜索时间来获得更高的召回率。

image.png

召回率与索引向量数量的关系:召回率是衡量搜索结果与使用IndexFlatL2进行详尽搜索的匹配程度的指标。

值得注意的是,即使使用nbits值为768,LSH的搜索速度也只是略快于平面Flat索引。

image.png

搜索时间的比较:展示了不同索引大小和nbits值下,IndexFlatL2的搜索时间相对于其自身在不同条件下的变化。

在实际应用中,一个更现实的召回率目标是接近40%,同时保持合理的速度提升。数据集的大小和维度对LSH的性能有显著影响。随着维度的增加,为了保持准确性,可能需要使用更高的nbits值,但这仍有可能实现更快的搜索速度。关键在于为每个用例和数据集找到正确的平衡点。向量相似性搜索是一个多样化的领域,Flat索引和LSH只是众多选择中的两种。选择正确的索引策略需要结合实验和专业知识。

在相似性搜索中,始终需要在不同的索引选项和参数设置之间寻找最佳解决方案,这是一种平衡的行为。

总结

选择正确的相似性搜索算法取决于多种因素,包括数据集的大小和维度、搜索性能的要求,以及准确性的容忍度。LSH是众多工具中的一个,它在某些情况下表现出色,但也可能需要与其他技术相结合以达到最佳效果。除了LSH,还有许多其他算法适合于高效的相似性搜索,例如:

  • HNSW(Hierarchical Navigable Small World):提供在大规模数据集上进行近似最近邻搜索的能力。
  • IVF(Inverted File Index):通过聚类和索引减少搜索范围,提高了搜索速度。
  • PQ(Product Quantization):一种量化技术,通过将向量空间划分为较小的子空间来加速搜索。

鼓励读者根据本文提供的信息,进行实验和探索,以找到最适合自己特定需求的搜索算法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/749404.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024平价蓝牙耳机推荐哪款?百元左右平价蓝牙耳机推荐

在2024的无线耳机市场中&#xff0c;蓝牙耳机已经成为了主流。无论是对于音乐爱好者还是普通消费者&#xff0c;选择一款音质出色、舒适度高且功能齐全的蓝牙耳机还是很重要的。一款好的蓝牙耳机不仅戴在耳朵上很舒服&#xff0c;而且音质还没有任何的杂音&#xff0c;但现在的…

FPGA学习笔记(6)——硬件调试与网表添加探针

对信号进行分析&#xff0c;除了使用内置的ILA IP核&#xff0c;还可以在网表中添加探针。 本节采用之前配置的LED灯闪烁代码&#xff0c;对原始工程进行修改。 如果是新建工程&#xff0c;需要现将代码进行综合Synthesis&#xff0c;然后再进行接下来的操作。 1、点击Open S…

Android 根证书管理与证书验证

大部分的安卓应用都免不了与后端服务器进行通信。在通信过程中&#xff0c;主要面临两方面的风险&#xff1a;1、中间人攻击。当通信使用 HTTP 等明文协议&#xff0c;通信内容可被嗅探甚至篡改。2、通信内容被攻击者分析。使用加密的协议&#xff0c;虽然避免了中间人攻击&…

PFA铲子聚四氟乙烯物料特氟龙铲粉料铲耐酸碱无污染塑料

PFA铲子:又称四氟铲子、聚四氟乙烯物料铲、特氟龙铲子; 常用尺寸型号 全长x宽x高(mm)165x57x31mm&#xff0c;一体成型&#xff0c;产品坚固&#xff0c;可以直接与食品接触。常用于制药厂&#xff0c;实验室等转移物料&#xff0c;铲取药品化学物品等&#xff0c;可以直接接触…

LangChain4j之HelloWorld

什么是LangChain4j 它是Java版本的LangChain&#xff0c;随着大模型的不断发展&#xff0c;如何在程序中更好的利用大模型的能力来提高编程效率是一种趋势&#xff0c;LangChain是这么自己介绍自己的&#xff1a; LangChain gives developers a framework to construct LLM‑p…

【Redis一】Redis配置与优化

目录 一.关系型数据库与非关系型数据库 1.关系型数据库 2.非关系型数据库 3.二者区别 4.非关系型数据库产生背景 5.NoSQL与SQL数据记录对比 关系型数据库 非关系型数据库 二.Redis相关概述 1.简介 2.五大数据类型 3.优缺点 3.1.优点 3.2.缺点 4.使用场景 5.采用…

阿里巴巴向国际用户开放人工智能模型平台ModelScope(魔搭社区)

阿里巴巴对 Hugging Face 和 Amazon Bedrock 的回应包含 5,000 多个中国专业模型&#xff0c;以及 1,500 个工具包和数据集 阿里云已将其人工智能模型存储库ModelScope&#xff08;魔搭社区&#xff09;的访问权限扩展至全球英语用户&#xff0c;意在吸引更多国际企业和开发者…

床旁交互,全视通打造以患者为中心的智慧病房

随着我国医疗建设的发展&#xff0c;医疗服务体系的不断建立健全&#xff0c;新形势下人们的医疗需求发生变化&#xff0c;医疗服务理念正逐步从传统的“以疾病为中心”向“以患者为中心”转变。 基于医院临床实际应用场景&#xff0c;在兼具实用性的前提下&#xff0c;建设了床…

yolov5驾驶员不规范行为检测

1 项目介绍 1.1 摘要 随着汽车工业的迅速发展和交通拥堵的加剧&#xff0c;驾驶员在行车过程中的不规范行为成为了导致交通事故频发的重要因素之一。为了减少交通事故的发生&#xff0c;保障道路安全&#xff0c;提高驾驶员的行车安全意识&#xff0c;本研究致力于实现驾驶员…

德国威步的技术演进之路(上):从软件保护到用户体验提升

德国威步自1989年成立以来一直专注于数字安全技术的研究和发展&#xff0c;在软件保护和数字授权领域树立了行业标杆&#xff0c;并在云端许可管理和物联网安全技术方面不断创新。德国威步的成就彰显了其对安全、创新和可持续发展的坚定追求。 德国威步将“完美保护、完美授权…

webpack+webpack server入门

​ 1.webpack介绍 webpack是一个模块加载器兼打包工具。它是以 commonJS 的形式来书写脚本的&#xff0c;但对 AMD/CMD 的支持也很全面&#xff0c;方便旧项目进行代码迁移。支持对react热插拔。 2.安装&#xff08;使用淘宝镜像&#xff09; 全局安装 cnpm install webpa…

Redis-数据类型-Set(不允许重复)

文章目录 1、查看redis是否启动2、通过客户端连接redis3、切换到2数据库4、给key指定的set集合中存入数据&#xff0c;set会自动去重5、返回可以指定的set集合中所有的元素6、返回集合中元素的数量(set cardinality)7、检查当前指定member是否是集合中的元素8、从集合中删除元素…

数学类-课程资料推荐-中科大教师首页

http://staff.ustc.edu.cn/~rui/cn/rui-course.html 数学分析讲义&#xff08;第一册&#xff09; (ustc.edu.cn)

BIO、NIO编程深入理解与直接内存、零拷贝

网路编程基本常识 一. Socket 什么是Socket Socket是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。它提供了应用层进程利用网络协议交换数据的机制&#xff0c;是应用程序与网络协议栈进行交互的接口。 说白了&#xff0c;Socket就是把TCP/IP协议族进行封装…

数通云网架构师涨薪班毕业都有哪些工作企业和岗位?

数通云网架构师涨薪班课程学完后&#xff0c;学员具备全行业全场景交付数通项目的能力&#xff0c;胜任企业网&#xff0c;广域网&#xff0c;数据中心网络等各种网络项目的交付能力&#xff0c;技术能力一项能够匹配年薪达30w-40w以上网络工程师岗位。 与誉天进行人才培养&…

Wordpress图像编辑插件-palleon v3.8.1中文版语言包

Palleon是一个强大的WordPress图像编辑器&#xff0c;可以与您的WordPress网站无缝集成&#xff0c;让您快速高效地工作。它拥有为你的WordPress网站创建令人惊叹的图像所需的一切。 Palleon让您完全控制图像&#xff0c;允许您逐个像素进行更改。您可以轻松地裁剪、调整图像大…

Android jetpack Room的简单使用

文章目录 项目添加ksp插件添加 room 引用开始使用room1. 创建bean2. 创建 dao类3. 创建database类 数据库升级复制数据库到指定路径参考文献 项目添加ksp插件 注意&#xff0c;因为ksp插件 是跟项目中使用的kotlin的版本要保持一致的&#xff0c;否则会报错的 首先我们去 https…

高德.js2.0绘制多条折线(轨迹)及清除所有折线

2.0版本的地图,需要绘制多条折线的时候,就需要循环生成,因此也需要循环清除 for (let j 0; j < combinedArray.length; j) {const item combinedArray[j];this.polyline new AMap.Polyline({map: this.map,path: item,showDir: true,strokeColor: "#28F", //线…

免费Logo在线生成:必试的6款工具

logo对企业来说非常重要。一个好的logo免费设计在线生成器往往会给企业带来无形的利润。因此&#xff0c;许多企业非常重视自己公司的logo。作为一名设计师&#xff0c;如果能找到一个好的logo免费设计在线生成器&#xff0c;势必会给实际的logo设计带来事半功倍的效果。本文精…

软件测试面试题:Web View如何测试?

Web View介绍 Web View&#xff08;网页视图&#xff09;是一种用于在应用程序中显示网页内容的组件或控件。提供了一种将网页内容嵌入到应用程序中的方式&#xff0c;使用户能够在应用程序中浏览和交互网页。 Web View通常用于开发移动应用程序&#xff0c;特别是混合应用程…