掌握 BM25:深入了解算法及其在 Milvus 中的应用

news2024/11/18 11:25:40

我们可以通过 Milvus 轻松实现 BM25 算法,将文档和查询转化为稀疏向量。然后,这些稀疏向量可用于向量搜索,根据特定查询找到最相关的文档。

信息检索算法在搜索引擎中非常重要,可确保搜索结果与用户的查询相关。

想象一下,我们正在寻找一个特定主题的研究论文。当我们在搜索引擎中输入 "RAG "作为关键词时,我们显然希望得到的是关于不同 RAG 方法的论文集,而不是其他一般的 NLP 方法。这就是信息检索算法发挥作用的地方。它们能确保我们获得与关键词高度相关的论文。

有两种常用的信息检索算法:TF-IDF 和 BM25。在本文中,我们将讨论有关 BM25 的一切。不过,在深入探讨之前,我们最好先了解一下 TF-IDF 的基本原理,因为 BM25 基本上是 TF-IDF 的扩展。

TF-IDF 的基本原理

词频-反向文档频率(TF-IDF)是一种基于统计方法的信息检索算法。它衡量文档中的关键词相对于文档集合的重要性。

从其名称可以看出,TF-IDF 由两部分组成:术语频率(TF)和反向文档频率(IDF)。它们衡量的是不同的方面,但最终,我们会将这两个部分相乘,得到最终的分数,从而估算出文档中某个词的相关性。

传统 TF-IDF 的 TF 部分衡量文档中特定关键词的出现率。

上述等式非常直观:我们在文档中找到的关键词实例越多,TF 值就越高。

另一方面,IDF 部分衡量的是包含关键词的文档在文档集中所占的比例。换句话说,这一部分告诉我们有多少文档中出现了我们的关键词。

我们可以看到,关键词在文档中出现的频率越高,其 IDF 分数就越低。这是因为我们要惩罚 “a”、“an”、"is "等常用词,因为它们往往出现在许多文档中。

计算完 TF 和 IDF 分量后,我们将结果相乘,得到关键词的最终 TF-IDF 分数。

从上面的最后一个等式中,我们可以看出,TF-IDF 能够捕捉到关键词的重要性,如果关键词在某一文档中出现的频率很高,而在其他文档中出现的频率却很低,那么它就会获得更高的分数。

在给定用户关键词或查询时,搜索引擎可以使用 TF-IDF 分数来衡量关键词与文档集之间的相关性。然后,搜索引擎可以对文档进行排序,并将最相关的文档呈现给用户。

TF-IDF 的问题

TF-IDF 公式有两个问题可以改进。

首先,假设我们有一个查询词 "兔子 "和两个要比较的文档。在文档 A 中,"兔子 "出现了 10 次,而该文档有 1000 个单词。另一方面,我们的关键词在文档 B 中只出现了一次,而该文档有 10 个单词。

仔细观察后,我们就会发现 TF-IDF 的局限性。文档 A 的 TF 得分为 10,而文档 B 的得分为 1。因此,与文档 B 相比,文档 A 将被推荐给我们。

但是,如果考虑到文件的长度,我们可能会说,与文件 A 相比,我们更喜欢文件 B。

如果一篇文档很短,但只出现了一次 “兔子”,那就更能说明该文档确实在谈论兔子。反之,如果一篇文档有数千字,但其中有 10 次提到 “兔子”,我们就不能确定该文档是否具体谈论了兔子。

为了解决这个问题,TF-IDF 的规范化变体将 TF 除以文档长度。这样,TF 部分的 TF 等式就是这样的:

但仍有一个问题需要优化,那就是关键词饱和度。

如果我们看一下 TF-IDF 的 TF 部分,我们在文档中找到的关键词越多,TF 就会越高。这种关系是线性的,这似乎是件好事,因为我们希望不断奖励包含我们关键词的文档。但是,如果我们在一篇文档中找到关键词 "兔子 "400 次,这是否真的意味着这篇文档的相关性是另一篇只出现 200 次的文档的两倍呢?

我们可以说,如果关键字 "兔子 "在一篇文档中出现了这么多次,我们就可以说这篇文档肯定与我们的关键字相关。该文档中关键词的额外出现次数不应该线性地增加其相关性的可能性。

文档中关键字出现次数从 2 次到 4 次所造成的分数跃升应该比从 200 次到 400 次的跃升影响更大。而这正是 TF-IDF 公式目前所缺少的。

从上面的可视化图中可以看到,由于我们将文档中的字数固定为 100,TF-IDF 的 TF 分数随着关键词出现率的增加而线性增加。

那么,我们该如何改进 TF-IDF 呢?这就是我们需要 BM25 算法的地方。

BM25 的基本原理

最佳匹配 25 (BM25) 算法可以看作是对传统 TF-IDF 算法的改进,它解决了上一节提到的所有问题。

我们先来讨论关键词饱和的问题。TF-IDF 的 TF 部分会随着文档中关键词出现次数的增加而线性增长。从 2 个关键词到 4 个关键词的得分跃升与从 50 个关键词到 52 个关键词的得分跃升相同。

与 TF-IDF 相比,BM25 为 TF 部分引入了一个略有不同的公式,我们将逐一慢慢揭示每个组成部分,以便让我们更容易理解引擎盖下发生了什么。

首先,BM25 在其 TF 公式中引入了一个新参数,而不是仅仅依赖关键词的出现次数:

上述参数 k 的作用是控制关键词每次递增对 TF 分数的贡献。让我们看看下面的可视化图,了解 k 的影响。

正如您所看到的,最初出现的几个关键词对整体 TF 分数的影响很大。但是,随着我们的关键词在文档中出现的次数越来越多,它对整体 TF 分数的贡献就变得越来越无关紧要了。k 值越大,每次出现的关键词对 TF 分数的贡献增长就越慢。这就解决了 TF-IDF 与关键词饱和度相关的缺点。

与传统的 TF-IDF 相比,BM25 的另一个基本改进是 BM25 将文档长度考虑在内。有了 BM25,包含一个关键词的 10 字文档会比包含 10 个关键词的 1000 字文档更有价值。

术语 ∣D∣ 代表文档长度,而 avg(D) 代表语料库中文档的平均长度。我们已经可以看到文档标准化对 k 参数的影响。如果文档比平均长度短,TF/(TF+k)的值就会增加,反之亦然。换句话说,较短的文档会比较长的文档更快接近饱和点。

不过,我们不能以同样的方式对待所有语料库。在某些语料库中,文档的长度非常重要,而在另一些语料库中,文档的长度则根本不重要。因此,我们在 TF 公式中引入了一个附加参数 b,以控制文档长度在总分中的重要性。


我们可以看到,如果我们将 b 的值设置为 0,那么 D/avgD 的比值就根本不会被考虑,这意味着我们并不重视文档的长度。而值为 1 则表示我们非常重视文档的长度。

以上等式是 BM25 中使用的 TF 部分的最终等式。那么 IDF 部分呢?

BM25 的 IDF 部分方程与 TF-IDF 稍有不同。BM25 的 IDF 公式定义如下:

其中,N 是语料库中的文档总数,DF 是包含关键词的文档数量。上述等式源于研究人员的经验观察,他们希望能推导出一个能够捕捉排名函数行为的等式,而不是简单地试验各种值,希望能达到最佳效果。

然而,这个 IDF 等式有一个很大的问题:如果我们在超过一半的语料库中找到我们的关键词,它就会得出一个负值。这在信息检索使用案例中毫无意义。在信息检索中,如果我们的关键词在语料库中的任何文档中都找不到,那么得分就不会低。

为了缓解这个问题,通常会在 IDF 公式中加入一个标量值 1,使其与 TF-IDF 的 IDF 项相同。

下面是 BM25 计算文档中特定关键词得分的最终公式:

顺便提一句:我们并没有真正讨论过上面 TF 部分分子中的 (k+1) 项。不过,由于这个项并不影响 BM25 整体结果的关系,因此可以忽略这个项。

BM25 的参数

在上一节中,我们了解到 BM25 有两个参数可以根据我们的使用情况进行调整:一个叫做 k,另一个叫做 b。

在实际应用中,k = 1.2 和 b = 0.75 这两个值在大多数语料库中都很有效。不过,您可以尝试使用这两个参数的值,以找到最适合您使用情况的值。这是因为选择 k 和 b 值遵循的是 "天下没有免费的午餐 "理论,也就是说,并不存在适用于所有用例的通用 "最佳 "k 值和 b 值。

根据实验结果,k 值在 0.5 至 2 的范围内趋于最佳,而 b 值在 0.3 至 0.9 的范围内趋于最佳。

在调整 k 值时,请自问:我的文档平均长度是多少?

如果您有一个很长的文档集,那么一个关键词很可能会出现好几次,即使文档并没有真正讨论这个关键词。在这种情况下,您可能希望将 k 值设置在一个较高的范围内,这样就不会太快达到 TF 分数的饱和点。反之亦然。如果文档的平均长度较短,那么您可能希望将 k 值设置在较低的范围内。

关于 b 值,请扪心自问:我有什么样的文档?在我的使用案例中,文档的长度是否会影响关键词的相关性?

例如,如果您有一个长篇科学文档集,那么这些文档中包含的大部分单词很可能都很重要。因此,您可能希望将 b 设置在较低的范围内。与此同时,如果您收集的是意见性和主观性文档,您可能希望将 b 值设置在较高的范围内,以惩罚短文档和长文档中潜在的关键字垃圾邮件。

TF-IDF 与 BM25 的比较

BM25 的确是对传统 TF-IDF 方法的改进。但这并不意味着我们应该使用 BM25 而不是 TF-IDF。在某些情况下,使用 TF-IDF 可能就足够了,因为它比 BM25 更简单,计算成本也更低。

以下是 TF-IDF 和 BM25 在相关性评分(TF-IDF 和 BM25 的最终结果)、精确度、处理较长文档的能力以及最佳使用方案等方面的对比分析列表。

方面传统 TF-IDFBM25
相关性评分- 依靠词频 (TF) 和反向文档频率 (IDF) 计算相关性得分。- 通过引入饱和项以及根据经验观察调整参数 kb,对 TF-IDF 进行了改进。
- 对文档中出现频率较高但在整个文档集中较少出现的术语赋予较高权重。- 通过考虑术语饱和度和文档长度正常化,提供更复杂的相关性评分,尤其是针对较长的文档。
精确度- 适用于较小的数据集或术语在文档中分布相对均匀的情况。- 比 TF-IDF 更稳健,更适应不同的数据集,能更有效地处理噪声或稀疏数据。
- 对于需要精确匹配的任务(如文档相似性或基于关键字的搜索)非常有效。- 在数据质量存在差异或文件集中存在异常值的情况下,可提高精确度。
处理较长的文件- 由于缺乏文件长度标准化,可能会偏向于较长的文件。- 通过将文档长度归一化,解决对较长文档的偏爱问题。
- 与 BM25 相比,较长文档的排名或相关性得分不够准确。- 为较长的文档提供更准确的排名和相关性评分。
场景- 简单易懂。- 稀疏和嘈杂的数据
- 完全匹配。- 较长的文件。

用 Milvus 实现 BM25

在本节中,我们将为语义搜索实现 BM25,并将整个过程与 Milvus 集成。如果您想跟进,请查看本笔记本。

在我们深入研究 Milvus 与 BM25 的集成之前,请务必安装 Milvus 单机版和 SDK。你可以在我们的 Milvus 文档页面找到完整的安装指南。

让我们先加载所有必要的库。

import pymilvus
import pandas as pd
from pymilvus import MilvusClient
from pymilvus import (
   utility,
   FieldSchema, CollectionSchema, DataType,
   Collection, AnnSearchRequest, RRFRanker, connections,
)
from pymilvus.model.sparse.bm25.tokenizers import build_default_analyzer
from pymilvus.model.sparse import BM25EmbeddingFunction

As the data, we'll use an e-commerce dataset that you can download freely on Kaggle. Let's load the dataset and instantiate BM25 model with Milvus.
df = pd.read_csv('.../amazon_products.csv')
df.columns

"""
Output:
Index(['asin', 'title', 'imgUrl', 'productURL', 'stars', 'reviews', 'price',
       'listPrice', 'category_id', 'isBestSeller', 'boughtInLastMonth'],
      dtype='object')
"""

如上图所示,我们的数据集有几列。出于向量搜索演示的目的,我们将只使用 title 列作为语料库。

BM25EmbeddingFunction 类是 Milvus 对 BM25 嵌入模型的实现,它能将文档或查询转换为稀疏嵌入表示。不过,在将任何文档和查询转化为稀疏嵌入之前,我们需要在语料库中拟合 BM25 模型,收集每个标记的统计数据。

analyzer = build_default_analyzer(language="en")

# Create corpus based on product title
corpus = df["title"].values.tolist()
corpus = [str(i) for i in corpus]

# Use the analyzer to instantiate the BM25EmbeddingFunction
bm25_ef = BM25EmbeddingFunction(analyzer)
# Fit the model on the corpus to get the statistics of the corpus
bm25_ef.fit(corpus)

上面的 build_default_analyzer 函数是 Milvus 的一个内置函数,它可以执行多种功能。首先,它会删除特定语言中常见的停顿词,对剩余的每个词进行标记化处理,然后收集每个标记相关性的统计数据。

现在,假设我们有 5 个产品标题。我们可以使用 encode_documents 方法将每个产品标题转化为稀疏向量表示。

product_title = ['7th Dragon III Code: VFD - Nintendo 3DS (Renewed)',
 'Mesh Bags Drawstring Bag Set - Nylon Mesh Drawstring Bags with Cord Lock Closure',
 'FIFA 20 Standard Edition - Xbox One',
 'Head Case Designs Officially Licensed Tom and Jerry Outdoor Chase Comic Graphics Vinyl Sticker Gaming',
 'Cloudz"The Big Bag" Travel & Sport Duffle Bag']

# Create embeddings for product title
product_title_embeddings = bm25_ef.encode_documents(product_title)

print("Sparse dim:", bm25_ef.dim, list(product_title_embeddings)[0].shape)
"""
Output:
Sparse dim: 673273 (1, 673273)
"""

每个文档的向量维数对应于我们的语料库中经过 build_default_analyzer 函数中的停止词过滤处理后的可用标记总数。每个向量元素代表文档中每个标记的相关性得分。

让我们将所有这些向量嵌入插入 Milvus 向量数据库。首先,我们要定义表的模式。然后,我们选择适当的索引和相似度指标,以便在进行向量搜索时使用。

由于 BM25 生成的是稀疏向量,因此我们可以使用倒排索引或 Weak-AND(WAND)算法等索引方法。不过,由于我们的稀疏向量具有高维性,因此我们可以使用倒排索引。这种方法可将每个维度映射到嵌入中的非零值,从而在搜索过程中直接访问相关数据。

作为向量搜索的度量指标,Milvus 目前只支持内积,因此我们将使用它。

connections.connect("default", host="localhost", port="19530")

fields = [
    FieldSchema(name="pk", dtype=DataType.VARCHAR,
                is_primary=True, auto_id=True, max_length=100),
    FieldSchema(name="product_title", dtype=DataType.VARCHAR, max_length=512),
    FieldSchema(name="product_title_vector", dtype=DataType.SPARSE_FLOAT_VECTOR),
]

schema = CollectionSchema(fields, "")
col = Collection("bm25_demo", schema)

sparse_index = {"index_type": "SPARSE_INVERTED_INDEX", "metric_type": "IP"}
col.create_index("product_title_vector", sparse_index)

# Insert data into schema
entities = [product_title, product_title_embeddings]
col.insert(entities)
col.flush()

现在我们可以执行矢量搜索了。假设我们有一个查询:“What product should I buy for traveling?” 然后,我们可以使用 encode_queries 将此查询转换为嵌入。接下来,我们可以执行向量搜索,以获得最相似的产品。

# Set up a Milvus client
client = MilvusClient(
    uri="<http://localhost:19530>"
)

collection = Collection("bm25_demo")      # Get an existing collection.
collection.load()

queries = ["What product should I buy for traveling"]
query_embeddings = bm25_ef.encode_queries(queries)

res = client.search(
    collection_name="bm25_demo",
    data=query_embeddings[0],
    anns_field="product_title_vector",
    limit=1,
    search_params={"metric_type": "IP", "params": {}},
    output_fields =["product_title"]
)

print(res)
"""
Output:
[[{'id': '449258959812952449', 'distance': 4.3612961769104, 'entity': {'product_title': 'Cloudz"The Big Bag" Travel & Sport Duffle Bag'}}]]
"""

我们找到了。根据搜索结果,与我们的查询最相似的产品是 “Cloudz “The Big Bag” Travel & Sport Duffle Bag”,考虑到我们拥有的产品标题目录,这是准确的。

使用 Milvus,您还可以执行其他复杂的矢量搜索。例如,您可以在进行矢量搜索的同时执行元数据过滤,以获得更高的搜索准确性。

此外,我们还可以执行混合搜索。这种搜索将 BM25 的稀疏向量与句子变形金刚或 OpenAI 的深度学习模型通常产生的密集向量相结合,从而提供更准确的产品推荐。

BM25 算法的进步

BM25 算法诞生后,研究人员提出了多种 BM25 优化变体,以扩展其在复杂信息检索场景中的功能。

第一个变体是 BM25F,即 “带字段的最佳匹配 25”。BM25F 对 BM25 算法进行了扩展,以处理具有结构化字段的文档,例如包含标题、正文、作者等多个字段的文档。在 BM25F 中,文档的每个字段都会被单独处理,每个字段的 BM25 分数都会被单独计算。然后使用加权和或其他汇总方法将这些分数合并,得出文档的最终相关性分数。

第二种是 BM25+ 或带相关性反馈的最佳匹配 25。在 BM25+ 中,来自之前搜索结果或用户互动的反馈信息被用来调整文档的相关性得分。用户给予正面评价或点击过的文档相关性得分较高,而给予负面评价的文档相关性得分较低。

结论

BM25 算法可以看作是对传统 TF-IDF 方法的改进,使其能够处理复杂的信息检索情况。BM25 算法的重要改进包括术语频率饱和和文档长度归一化。我们可以通过 BM25 变量(如 k 和 b)来调整这两个因素的影响。

我们可以通过 Milvus 轻松实现 BM25 算法,将文档和查询转化为稀疏向量。然后,这些稀疏向量可用于向量搜索,根据特定查询找到最相关的文档。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue2中watch与Vue3中watch对比和踩坑

上一节说到了 computed计算属性对比 &#xff0c;虽然计算属性在大多数情况下更合适&#xff0c;但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法&#xff0c;来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时&#…

conda环境下在pycharm中调试scrapy项目

前提条件 已经创建好了conda环境已经安装好了scrapy框架项目初始化完成 编写一个爬虫脚本 import scrapyclass StackOverflowSpider(scrapy.Spider):name stackoverflowstart_urls [http://stackoverflow.com/questions?sortvotes]def parse(self, response):print("…

阿一网络安全实战演练之利用 REST URL 中的服务器端参数污染

所需知识 要解决这个实验室问题&#xff0c;您需要了解以下内容&#xff1a; 如何确定用户输入是否包含在服务器端的 URL 路径或查询字符串中。如何使用路径遍历序列尝试更改服务器端请求。如何查找 API 文档。 这些内容在我们的 API 测试学院主题中有涵盖。 进入实验室 研…

终极解决CondaValueError: Malformed version string ‘~’: invalid character(s)问题

conda 创建环境时出现&#xff1a; Solving environment: failed CondaValueError: Malformed version string ‘~’: invalid character(s)以下两种方法都不行时&#xff1a; 原因一&#xff1a; 添加的镜像源中&#xff0c;清华镜像源是https&#xff08;错误&#xff09;&a…

软件测试 - 测试分类(静态测试、动态测试、白盒测试、黑盒测试、灰盒测试、单元测试、集成测试、系统测试、验收测试等)

一、为什么要对软件测试进⾏分类&#xff1f; 软件测试是软件⽣命周期中的⼀个重要环节&#xff0c;具有较⾼的复杂性&#xff0c;对于软件测试&#xff0c;可以从不同的⻆度 加以分类&#xff0c;使开发者在软件开发过程中的不同层次、不同阶段对测试⼯作进⾏更好的执⾏和管理…

R语言管道操作详解-高效编程

引言 R语言是一种广泛应用于统计分析和图形表示的编程语言和软件环境。随着数据分析和数据科学的发展&#xff0c;R语言的管道操作符已经成为提高代码可读性和效率的重要工具。本文将详细介绍R语言中的管道操作符&#xff0c;包括它们的用途、语法和一些实用的示例。 目录 引…

手写签名怎么变成电子签名?

教大家一个快速生成有效电子签名的方法&#xff01;&#xff08;有效电子签名即通过正轨平台绑定了CA数字证书、防伪防盗的签名&#xff09; 1.登录【微签】&#xff0c;点击【自己签】。 2.点【添加文件】&#xff0c;上传需要签名的电子文件&#xff08;格式不限&#xff09;…

一起学习LeetCode热题100道(46/100)

46.二叉树展开为链表(学习) 给你二叉树的根结点 root &#xff0c;请你将它展开为一个单链表&#xff1a; 展开后的单链表应该同样使用 TreeNode &#xff0c;其中 right 子指针指向链表中下一个结点&#xff0c;而左子指针始终为 null 。 展开后的单链表应该与二叉树 先序遍历…

Affine Transformations仿射变换

什么是仿射变换 仿射变换&#xff08;Affine Transformation&#xff09;是数学和计算机图形学中的一种线性变换&#xff0c;它包括了平移、旋转、缩放、剪切等操作。仿射变换保留了几何图形的“仿射性质”&#xff0c;即平行线在变换后仍然平行&#xff0c;线性组合在变换后仍…

电机预测性维护模组

设备简介 本模组为了对电机进行预测性运维而开发&#xff0c;可以采集电机的 3 路加速度振动传感器、3 路电流&#xff08;电机供电互感器输出信号&#xff09;、1 路转速&#xff08;电机转速&#xff09;、8 路温度&#xff08;PT100 温度传感器&#xff09;。 模组计算振动…

一文带你看懂安全生产管理系统

通过集成多种先进技术和设备&#xff0c;实现对企业安全生产全过程的智能化、精细化管理。系统分为五个核心层面&#xff0c;各层面相互协作&#xff0c;共同确保企业的安全生产。 1. 数据采集层 设备终端&#xff1a;利用防爆终端、防爆平板、无线传感器、电子标签、定位设备、…

WinForm DataGridView整行选中并且checkbox勾选

WinForm DataGridView选中行设置 文章目录 WinForm DataGridView选中行设置添加checkbox列列和选中行效果选中行代码 添加checkbox列 列和选中行效果 选中行代码 public Basic_configuration(){InitializeComponent();...........//添加鼠标事件this.dataGridView_basic.CellMo…

node.js: mssql2019 sequelize6 es6+ ORM in vscode and WebStorm 2023.1

mssql: insert into [dbo].[tutorials]([title],[description],[published],[createdAt],[updatedAt]) values(N涂聚文,N涂聚文,0,2025-05-04,2025-05-04); go insert into [dbo].[tutorials]([title],[description],[published],[createdAt],[updatedAt]) values(Ngeovindu,N…

实战OpenCV之图像的属性

基础入门 图像的属性指的是描述图像基本信息的数据&#xff0c;包括但不限于&#xff1a;图像的尺寸、颜色通道数、像素数据类型等。这些属性对于图像处理非常重要&#xff0c;因为它们直接关系到如何正确地读取、处理和存储图像。常见的图像属性包括&#xff1a; 尺寸&#xf…

WandB 简明教程【Weights Bias】

在机器学习实验领域&#xff0c;调整超参数类似于微调复杂机器的旋钮和刻度盘。这些参数通常很微妙但至关重要&#xff0c;能够显著影响我们模型的性能和行为。WandB&#xff08;权重和偏差 ) 是一个强大的在线工具集&#xff0c;旨在简化模型训练、评估和分析的过程。 随着我…

TCP shutdown 之后~

目录 摘要 1 API 2 shutdown(sockfd, SHUT_WR) 3 shutdown(sockfd, SHUT_WR) 4 kernel 是怎么做的&#xff1f; 附 摘要 通过 shutdown() 关闭读写操作&#xff0c;会发生什么&#xff1f;具体点呢&#xff0c;考虑两个场景&#xff1a; 场景一&#xff1a;C 发送数据完毕…

VBA技术资料MF184:图片导入Word添加说明文字设置格式

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

C ++初阶:C++入门级知识点

&#x1f37a;0.前言 言C之言&#xff0c;聊C之识&#xff0c;以C会友&#xff0c;共向远方。各位博友的各位你们好啊&#xff0c;这里是持续分享C知识的小赵同学&#xff0c;今天要分享的C知识是C入门知识点&#xff0c;在这一章&#xff0c;小赵将会向大家展开聊聊C入门知识…

基于Mediapipe的手势识别系统 | OpenCV | Mediapipe | C++ | QT | Python | C# | Unity

基于Mediapipe的手势识别系统 OpenCV、Mediapipe C (QT)、Python (PyCharm)、C# (Visual Studio) Unity 3D 登录界面 图片手势识别 视频文件手势识别 摄像头实时手势识别 演示视频 基于Mediapipe的手势识别系统

UDP和TCP协议段格式分析

目录 UDP协议 特点 UDP协议的缓冲区 UDP协议段格式 TCP协议 特点 如何理解TCP是传输控制协议&#xff1f; TCP协议段格式 四位首部长度 16位窗口大小 32位序号 32位确认序号 TCP/IP四层模型&#xff1a; UDP协议 UDP&#xff08;User Datagram Protocol &#xff…