LLM之RAG实战(三十)| 探索RAG语义分块策略
在LLM之RAG实战(二十九)| 探索RAG PDF解析解析文档后,我们可以获得结构化或半结构化的数据。现在的主要任务是将它们分解成更小的块来提取详细的特征,然后嵌入这些特征来表示它们的语义,其在RAG中的位置如图1所示:
最常用的分块方法是基于规则的,采用固定的块大小或相邻块的重叠等技术。对于多级文档,我们可以使用Langchain提供的RecursiveCharacterTextSplitter[1]来定义多级分隔符。
然而,在实际应用中,由于严格的预定义规则(块大小或重叠部分的大小),基于规则的分块方法很容易导致检索上下文不完整或包含噪声的块大小过大等问题。
因此,对于分块,最优雅的方法显然是基于语义的分块。语义分块旨在确保每个分块包含尽可能多的语义独立信息。
本文将探讨语义分块的方法,并解释了它们的原理和应用。我们将介绍三种类型的方法:
- Embedding-based
- Model-based
- LLM-based
一、基于Embedding的方法
LlamaIndex和Langchain都提供了一个基于embedding的语义分块器。这两个框架的实现思路基本是一样的,我们将以LlamaIndex为例进行介绍。
请注意,要访问LlamaIndex中的语义分块器,您需要安装最新的版本。我安装的前一个版本0.9.45没有包含此算法。因此,我创建了一个新的conda环境,并安装了更新版本0.10.12:
pip install llama-index-core
pip install llama-index-readers-file
pip install llama-index-embeddings-openai
值得一提的是,LlamaIndex的0.10.12可以灵活安装,因此这里只安装了一些关键组件。安装的版本如下:
(llamaindex_010) Florian:~ Florian$ pip list | grep llama
llama-index-core 0.10.12
llama-index-embeddings-openai 0.1.6
llama-index-readers-file 0.1.5
llamaindex-py-client 0.1.13
测试代码如下:
from llama_index.core.node_parser import (
SentenceSplitter,
SemanticSplitterNodeParser,
)
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core import SimpleDirectoryReader
import os
os.environ["OPENAI_API_KEY"] = "YOUR_OPEN_AI_KEY"
# load documents
dir_path = "YOUR_DIR_PATH"
documents = SimpleDirectoryReader(dir_path).load_data()
embed_model = OpenAIEmbedding()
splitter = SemanticSplitterNodeParser(
buffer_size=1, breakpoint_percentile_threshold=95, embed_model=embed_model
)
nodes = splitter.get_nodes_from_documents(documents)
for node in nodes:
print('-' * 100)
print(node.get_content())
splitter.get_nodes_from_documents函数的主要过程如图2所示:
图2中提到的“sentences”是一个python列表,其中每个成员都是一个字典,包含四个(键、值)对,键的含义如下:
- sentences:当前句子;
- index:当前句子的序号;
- combined_sentence:一个滑动窗口,包括[index-self-buffer_size,index,index+self.buffer_size]3句话(默认情况下,self-buffer_size=1)。它是一种用于计算句子之间语义相关性的工具。组合前句和后句的目的是减少噪音,更好地捕捉连续句子之间的关系;
- combined_sentence_embedding:combined_sentence的嵌入。
从以上分析中可以明显看出,基于embedding的语义分块本质上包括基于滑动窗口(combined_sentence)计算相似度。那些相邻的并且满足阈值的句子被分类到一个块中。
下面我们使用BERT论文[2]作为目录路径,以下是一些运行结果:
(llamaindex_010) Florian:~ Florian$ python /Users/Florian/Documents/june_pdf_loader/test_semantic_chunk.py
...
...
----------------------------------------------------------------------------------------------------
We argue that current techniques restrict the
power of the pre-trained representations, espe-
cially for the fine-tuning approaches. The ma-
jor limitation is that standard language models are
unidirectional, and this limits the choice of archi-
tectures that can be used during pre-training. For
example, in OpenAI GPT, the authors use a left-to-
right architecture, where every token can only at-
tend to previous tokens in the self-attention layers
of the Transformer (Vaswani et al., 2017). Such re-
strictions are sub-optimal for sentence-level tasks,
and could be very harmful when applying fine-
tuning based approaches to token-level tasks such
as question answering, where it is crucial to incor-
porate context from both directions.
In this paper, we improve the fine-tuning based
approaches by proposing BERT: Bidirectional
Encoder Representations from Transformers.
BERT alleviates the previously mentioned unidi-
rectionality constraint by using a “masked lan-
guage model” (MLM) pre-training objective, in-
spired by the Cloze task (Taylor, 1953). The
masked language model randomly masks some of
the tokens from the input, and the objective is to
predict the original vocabulary id of the maskedarXiv:1810.04805v2 [cs.CL] 24 May 2019
----------------------------------------------------------------------------------------------------
word based only on its context. Unlike left-to-
right language model pre-training, the MLM ob-
jective enables the representation to fuse the left
and the right context, which allows us to pre-
train a deep bidirectional Transformer. In addi-
tion to the masked language model, we also use
a “next sentence prediction” task that jointly pre-
trains text-pair representations. The contributions
of our paper are as follows:
• We demonstrate the importance of bidirectional
pre-training for language representations. Un-
like Radford et al. (2018), which uses unidirec-
tional language models for pre-training, BERT
uses masked language models to enable pre-
trained deep bidirectional representations. This
is also in contrast to Peters et al.
----------------------------------------------------------------------------------------------------
...
...
基于embedding的方法:总结
- 测试结果表明,块的粒度相对较粗。
- 图2还显示了这种方法是基于页面的,并且没有直接解决跨越多个页面的块的问题。
- 通常,基于嵌入的方法的性能在很大程度上取决于嵌入模型。实际效果需要进一步评估。
二、基于模型的方法
2.1 Naive BERT
回忆一下BERT的预训练过程,其中有个二元分类任务(NSP)来让模型学习两个句子之间的关系。两个句子同时输入到BERT中,并且该模型预测第二个句子是否在第一个句子之后。
我们可以将这一原理应用于设计一种简单的分块方法。对于文档,请将其拆分为多个句子。然后,使用滑动窗口将两个相邻的句子输入到BERT模型中进行NSP判断,如图3所示:
如果预测得分低于预设阈值,则表明两句之间的语义关系较弱。这可以作为文本分割点,如图3中句子2和句子3之间所示。
这种方法的优点是可以直接使用,而不需要训练或微调。
然而,这种方法在确定文本分割点时只考虑前句和后句,忽略了来自其他片段的信息。此外,这种方法的预测效率相对较低。
2.2 Cross Segment Attention
论文《Text Segmentation by Cross Segment Attention》[3]提出了三种跨段注意力模型,如图4所示:
图4(a)显示了跨段BERT模型,该模型将文本分割定义为逐句分类任务。潜在中断的上下文(两侧的k个令牌)被输入到模型中。将与[CLS]相对应的隐藏状态传递给softmax分类器,以做出关于在潜在断点处进行分割的决定。
本论文还提出了另外两个模型:一种是使用BERT模型来获得每个句子的向量表示。然后,将多个连续句子的这些向量表示输入到Bi-LSTM(图4(b))或另一个BERT(图4),以预测每个句子是否是文本分割边界。
当时,这三个模型取得了SOTA的结果,如图5所示:
然而,到目前为止,只发现了本论文的训练代码[4],推理模型没有找到。
2.3 SeqModel
跨段模型独立地对每个句子进行矢量化,不考虑任何更广泛的上下文信息。SeqModel中提出了进一步的增强,如论文“Sequence Model with Self-Adaptive Sliding Window for Efficient Spoken Document Segmentation”[5]中所述。
SeqModel[6]使用BERT同时对多个句子进行编码,在计算句子向量之前对较长上下文中的依赖关系进行建模。然后它预测文本分割是否发生在每个句子之后。此外,该模型利用自适应滑动窗口方法在不影响精度的情况下提高推理速度。SeqModel的示意图如图6所示。
SeqModel可以通过ModelScope[7]框架使用。代码如下所示:
from modelscope.outputs import OutputKeys
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
p = pipeline(
task = Tasks.document_segmentation,
model = 'damo/nlp_bert_document-segmentation_english-base'
)
print('-' * 100)
result = p(documents='We demonstrate the importance of bidirectional pre-training for language representations. Unlike Radford et al. (2018), which uses unidirectional language models for pre-training, BERT uses masked language models to enable pretrained deep bidirectional representations. This is also in contrast to Peters et al. (2018a), which uses a shallow concatenation of independently trained left-to-right and right-to-left LMs. • We show that pre-trained representations reduce the need for many heavily-engineered taskspecific architectures. BERT is the first finetuning based representation model that achieves state-of-the-art performance on a large suite of sentence-level and token-level tasks, outperforming many task-specific architectures. Today is a good day')
print(result[OutputKeys.TEXT])
测试数据最后附加了一句话,“Today is a good day”,但结果并没有把“Today is a good day”分开。
(modelscope) Florian:~ Florian$ python /Users/Florian/Documents/june_pdf_loader/test_seqmodel.py
2024-02-24 17:09:36,288 - modelscope - INFO - PyTorch version 2.2.1 Found.
2024-02-24 17:09:36,288 - modelscope - INFO - Loading ast index from /Users/Florian/.cache/modelscope/ast_indexer
...
...
----------------------------------------------------------------------------------------------------
...
...
We demonstrate the importance of bidirectional pre-training for language representations.Unlike Radford et al.(2018), which uses unidirectional language models for pre-training, BERT uses masked language models to enable pretrained deep bidirectional representations.This is also in contrast to Peters et al.(2018a), which uses a shallow concatenation of independently trained left-to-right and right-to-left LMs.• We show that pre-trained representations reduce the need for many heavily-engineered taskspecific architectures.BERT is the first finetuning based representation model that achieves state-of-the-art performance on a large suite of sentence-level and token-level tasks, outperforming many task-specific architectures.Today is a good day
三、基于LLM的方法
论文《Dense X Retrieval: What Retrieval Granularity Should We Use?》[8]引入了一个新的检索单元,称为proposition。proposition被定义为文本中的原子表达式,每个命题都封装了一个不同的事实,并以简洁、自包含的自然语言格式呈现。
那么,我们如何获得这个所谓的命题呢?在本文中,它是通过构建提示和与LLM的交互来实现的。
LlamaIndex和Langchain都实现了相关的算法,下面使用LlamaIndex进行了演示。
LlamaIndex的实现思想包括使用论文中提供的提示生成命题:
PROPOSITIONS_PROMPT = PromptTemplate(
"""Decompose the "Content" into clear and simple propositions, ensuring they are interpretable out of
context.
1. Split compound sentence into simple sentences. Maintain the original phrasing from the input
whenever possible.
2. For any named entity that is accompanied by additional descriptive information, separate this
information into its own distinct proposition.
3. Decontextualize the proposition by adding necessary modifier to nouns or entire sentences
and replacing pronouns (e.g., "it", "he", "she", "they", "this", "that") with the full name of the
entities they refer to.
4. Present the results as a list of strings, formatted in JSON.
Input: Title: ¯Eostre. Section: Theories and interpretations, Connection to Easter Hares. Content:
The earliest evidence for the Easter Hare (Osterhase) was recorded in south-west Germany in
1678 by the professor of medicine Georg Franck von Franckenau, but it remained unknown in
other parts of Germany until the 18th century. Scholar Richard Sermon writes that "hares were
frequently seen in gardens in spring, and thus may have served as a convenient explanation for the
origin of the colored eggs hidden there for children. Alternatively, there is a European tradition
that hares laid eggs, since a hare’s scratch or form and a lapwing’s nest look very similar, and
both occur on grassland and are first seen in the spring. In the nineteenth century the influence
of Easter cards, toys, and books was to make the Easter Hare/Rabbit popular throughout Europe.
German immigrants then exported the custom to Britain and America where it evolved into the
Easter Bunny."
Output: [ "The earliest evidence for the Easter Hare was recorded in south-west Germany in
1678 by Georg Franck von Franckenau.", "Georg Franck von Franckenau was a professor of
medicine.", "The evidence for the Easter Hare remained unknown in other parts of Germany until
the 18th century.", "Richard Sermon was a scholar.", "Richard Sermon writes a hypothesis about
the possible explanation for the connection between hares and the tradition during Easter", "Hares
were frequently seen in gardens in spring.", "Hares may have served as a convenient explanation
for the origin of the colored eggs hidden in gardens for children.", "There is a European tradition
that hares laid eggs.", "A hare’s scratch or form and a lapwing’s nest look very similar.", "Both
hares and lapwing’s nests occur on grassland and are first seen in the spring.", "In the nineteenth
century the influence of Easter cards, toys, and books was to make the Easter Hare/Rabbit popular
throughout Europe.", "German immigrants exported the custom of the Easter Hare/Rabbit to
Britain and America.", "The custom of the Easter Hare/Rabbit evolved into the Easter Bunny in
Britain and America." ]
Input: {node_text}
Output:"""
)
在上一节基于嵌入的方法中,我们安装了LlamaIndex 0.10.12的关键组件。但如果我们想使用DenseXRetrievalPack,我们还需要运行pip install-lama-index-llms-openai。安装后,当前与LlamaIndex相关的组件如下:
(llamaindex_010) Florian:~ Florian$ pip list | grep llama
llama-index-core 0.10.12
llama-index-embeddings-openai 0.1.6
llama-index-llms-openai 0.1.6
llama-index-readers-file 0.1.5
llamaindex-py-client 0.1.13
测试代码如下:
from llama_index.core.readers import SimpleDirectoryReader
from llama_index.core.llama_pack import download_llama_pack
import os
os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_KEY"
# Download and install dependencies
DenseXRetrievalPack = download_llama_pack(
"DenseXRetrievalPack", "./dense_pack"
)
# If you have already downloaded DenseXRetrievalPack, you can import it directly.
# from llama_index.packs.dense_x_retrieval import DenseXRetrievalPack
# Load documents
dir_path = "YOUR_DIR_PATH"
documents = SimpleDirectoryReader(dir_path).load_data()
# Use LLM to extract propositions from every document/node
dense_pack = DenseXRetrievalPack(documents)
response = dense_pack.run("YOUR_QUERY")
通过上述测试代码,学会了初步使用类DenseXRetrievalPack,下面分析一下类DenseXRetrievalPack的源代码:
class DenseXRetrievalPack(BaseLlamaPack):
def __init__(
self,
documents: List[Document],
proposition_llm: Optional[LLM] = None,
query_llm: Optional[LLM] = None,
embed_model: Optional[BaseEmbedding] = None,
text_splitter: TextSplitter = SentenceSplitter(),
similarity_top_k: int = 4,
) -> None:
"""Init params."""
self._proposition_llm = proposition_llm or OpenAI(
model="gpt-3.5-turbo",
temperature=0.1,
max_tokens=750,
)
embed_model = embed_model or OpenAIEmbedding(embed_batch_size=128)
nodes = text_splitter.get_nodes_from_documents(documents)
sub_nodes = self._gen_propositions(nodes)
all_nodes = nodes + sub_nodes
all_nodes_dict = {n.node_id: n for n in all_nodes}
service_context = ServiceContext.from_defaults(
llm=query_llm or OpenAI(),
embed_model=embed_model,
num_output=self._proposition_llm.metadata.num_output,
)
self.vector_index = VectorStoreIndex(
all_nodes, service_context=service_context, show_progress=True
)
self.retriever = RecursiveRetriever(
"vector",
retriever_dict={
"vector": self.vector_index.as_retriever(
similarity_top_k=similarity_top_k
)
},
node_dict=all_nodes_dict,
)
self.query_engine = RetrieverQueryEngine.from_args(
self.retriever, service_context=service_context
)
如代码所示,首先使用text_splitter将文档划分为原始nodes,然后调用self._gen_propositions来获得相应的sub_nodes。然后,它使用nodes+sub_nodes构建VectorStoreIndex,该索引可以通过RecursiveRetriever进行检索。递归检索器可以使用小块进行检索,但它会将相关的大块传递到生成阶段。
测试文档仍然是BERT论文。通过调试,我们发现sub_nodes[].text不是原来的文本,它们被重写了:
> /Users/Florian/anaconda3/envs/llamaindex_010/lib/python3.11/site-packages/llama_index/packs/dense_x_retrieval/base.py(91)__init__()
90
---> 91 all_nodes = nodes + sub_nodes
92 all_nodes_dict = {n.node_id: n for n in all_nodes}
ipdb> sub_nodes[20]
IndexNode(id_='ecf310c7-76c8-487a-99f3-f78b273e00d9', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='Our paper demonstrates the importance of bidirectional pre-training for language representations.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
ipdb> sub_nodes[21]
IndexNode(id_='4911332e-8e30-47d8-a5bc-ed7cbaa8e042', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='Radford et al. (2018) uses unidirectional language models for pre-training.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
ipdb> sub_nodes[22]
IndexNode(id_='83aa82f8-384a-4b06-92c8-d6277c4162bf', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='BERT uses masked language models to enable pre-trained deep bidirectional representations.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
ipdb> sub_nodes[23]
IndexNode(id_='2ac635c2-ccb0-4e62-88c7-bcbaef3ef38a', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='Peters et al. (2018a) uses a shallow concatenation of independently trained left-to-right and right-to-left LMs.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
ipdb> sub_nodes[24]
IndexNode(id_='e37b17cf-30dd-4114-a3c5-9921b8cf0a77', embedding=None, metadata={}, excluded_embed_metadata_keys=[], excluded_llm_metadata_keys=[], relationships={}, text='Pre-trained representations reduce the need for many heavily-engineered task-specific architectures.', start_char_idx=None, end_char_idx=None, text_template='{metadata_str}\n\n{content}', metadata_template='{key}: {value}', metadata_seperator='\n', index_id='8deca706-fe97-412c-a13f-950a19a594d1', obj=None)
sub_nodes和nodes之间的关系如图7所示,是一个从小到大的索引结构。
从小到大的索引结构是通过 self._gen_propositions构建的,代码如下:
async def _aget_proposition(self, node: TextNode) -> List[TextNode]:
"""Get proposition."""
inital_output = await self._proposition_llm.apredict(
PROPOSITIONS_PROMPT, node_text=node.text
)
outputs = inital_output.split("\n")
all_propositions = []
for output in outputs:
if not output.strip():
continue
if not output.strip().endswith("]"):
if not output.strip().endswith('"') and not output.strip().endswith(
","
):
output = output + '"'
output = output + " ]"
if not output.strip().startswith("["):
if not output.strip().startswith('"'):
output = '"' + output
output = "[ " + output
try:
propositions = json.loads(output)
except Exception:
# fallback to yaml
try:
propositions = yaml.safe_load(output)
except Exception:
# fallback to next output
continue
if not isinstance(propositions, list):
continue
all_propositions.extend(propositions)
assert isinstance(all_propositions, list)
nodes = [TextNode(text=prop) for prop in all_propositions if prop]
return [IndexNode.from_text_node(n, node.node_id) for n in nodes]
def _gen_propositions(self, nodes: List[TextNode]) -> List[TextNode]:
"""Get propositions."""
sub_nodes = asyncio.run(
run_jobs(
[self._aget_proposition(node) for node in nodes],
show_progress=True,
workers=8,
)
)
# Flatten list
return [node for sub_node in sub_nodes for node in sub_node]
对于每个原始node,异步调用self_aget_proposition通过PROPOSITIONS_PROMPT获取LLM的返回inital_output,然后基于inital_out获取命题并构建TextNode。最后,将这些TextNode与原始node相关联,即[IndexNode.from_text_node(n,node.node_id)for n in nodes]。
值得一提的是,原始论文使用LLM生成的命题作为训练数据来进一步微调文本生成模型。文本生成模型[9]现在可以公开访问。感兴趣的读者可以尝试一下。
基于LLM的方法:综述
一般来说,这种使用LLM构造命题的分块方法实现了更精细的分块。它与原始节点形成了一个从小到大的索引结构,从而为语义分块提供了一个新的思路。然而,这种方法依赖于LLM,这是相对昂贵的。
四、结论
本文探讨了三种类型的语义分块方法的原理和实现方法,并提供了一些综述。
一般来说,语义分块是一种更优雅的方式,也是优化RAG的关键。
参考文献:
[1] https://github.com/langchain-ai/langchain/blob/v0.1.9/libs/langchain/langchain/text_splitter.py#L851C1-L851C6
[2] https://arxiv.org/pdf/1810.04805.pdf
[3] https://arxiv.org/abs/2004.14535
[4] https://github.com/aakash222/text-segmentation-NLP/
[5] https://arxiv.org/pdf/2107.09278.pdf
[6] https://github.com/alibaba-damo-academy/SpokenNLP
[7] https://github.com/modelscope/modelscope/
[8] https://arxiv.org/pdf/2312.06648.pdf
[9] https://github.com/chentong0/factoid-wiki
相关文章:

LLM之RAG实战(三十)| 探索RAG语义分块策略
在LLM之RAG实战(二十九)| 探索RAG PDF解析解析文档后,我们可以获得结构化或半结构化的数据。现在的主要任务是将它们分解成更小的块来提取详细的特征,然后嵌入这些特征来表示它们的语义,其在RAG中的位置如图1所示&…...

软件测试-------Web(性能测试 / 界面测试 / 兼容性测试 / 安全性测试)
Web(性能测试 / 界面测试 / 兼容性测试 / 安全性测试) 一、Web性能测试:(压力测试、负载测试、连接速度测试)1、压力测试: 并发测试 (如500人同时登录邮箱) 2、负载测试…...

工欲善其事,必先利其器,Markdown和Mermaid的梦幻联动(2)
该文章Github地址:https://github.com/AntonyCheng/typora-notes/tree/master/chapter03-mermaid 在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文…...

STM32基础--使用寄存器点亮流水灯
GPIO 简介 GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO被分成很多组,每组有 16 个引脚…...

代码随想录训练营Day25:● 216.组合总和III ● 17.电话号码的字母组合
216.组合总和III 题目链接 https://leetcode.cn/problems/combination-sum-iii/description/ 题目描述 思路 自己写的效率会慢一些,而且没有用到剪枝 class Solution {List<List<Integer>> list new ArrayList<>();List<Integer> lis…...

SwiftUI的 特性 - ViewModify
SwiftUI的 特性 - ViewModify 记录一下SwiftUI的 特性 - ViewModify的使用方式 可以通过viewModify来管理视图的样式,结合extension来完成封装达到解偶效果 import SwiftUI/// 我们可以通过viewModify来管理视图的样式,来达到解偶效果 struct DefaultB…...

中间件 | RPC - [Dubbo]
INDEX 1 Dubbo 与 web 容器的关系2 注册发现流程3 服务配置3.1 注册方式 & 订阅方式3.2 服务导出3.3 配置参数 4 底层技术4.1 Dubbo 的 spi 机制4.2 Dubbo 的线程池4.3 Dubbo 的负载均衡策略4.3 Dubbo 的协议 1 Dubbo 与 web 容器的关系 dubbo 本质上是一个 RPC 框架&…...

【中等】保研/考研408机试-二叉树相关
目录 一、基本二叉树 1.1结构 1.2前序遍历(注意三种遍历中Visit所在的位置) 1.2中序遍历 1.3后序遍历 二、真题实战 2.1KY11 二叉树遍历(清华大学复试上机题)【较难】 2.2KY212 二叉树遍历二叉树遍历(华中科技大…...

自动驾驶---Motion Planning之构建SLT Driving Corridor
1 背景 在上篇博客《自动驾驶---Motion Planning之Speed Boundary》中,主要介绍了Apollo中Speed Boundary的一些内容,可以构造ST图得到边界信息,最后结合粗糙的速度曲线和路径曲线,即可使用优化的方法求解得到最终的轨迹信息(s,s,s,l,l,l)。 本篇博客笔者主要介绍近…...

本地文件包含漏洞利用
目录 前期信息收集获取网站权限获取服务器权限纵向提权 前期信息收集 拿到目标的资产,先试一下IP能不能访问 探测一下目标的端口运行的是什么服务 nmap -sC -sV xx.xx9.95.185 -Pn获取网站权限 我们可以知道目标的80端口上运行着http服务,服务器是u…...

【docker】docker的常用命令
📝个人主页:五敷有你 🔥系列专栏:中间件 ⛺️稳中求进,晒太阳 常规命令 docker version #查看docker 版本信息docker info #显示docker 的系统信息,包括镜像和容器数量docker --help #查看所有的命…...

jmeter实战
jmeter学习 1,接口在定义时,post请求参数尽量放在body里面,get请求参数尽量放在parameters里面,否则会导致jmeter请求接口报错的问题(jmeter底层有较为严格的请求格式) 2,定义全局变量使用:Config Elemen…...

面试官常问问题
1、请你简单的自我介绍一下? 【Tips】① 口述内容不可与简历内容冲突;②阐述方式避免过度官方 且语速较快;③言简意赅,直击要害,抓重点突出项;④面试前应自己模拟练习几次,避免过度紧张导致的口…...

外包就干了2个月,技术退步明显....
先说情况,大专毕业,18年通过校招进入湖南某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...

面向对象 汇总(详细内容见Day12—16)
面向对象 汇总(详细内容见Day12—16) 文章目录 面向对象 汇总(详细内容见Day12—16)一、概念二、类三、对象四、成员属性/成员变量五、成员方法六、构造方法七、private - 私有化八、封装九、this - 本对象十、分包十一、static -…...

结构体联合体枚举和位段
文章目录 结构体结构体类型的声明特殊的声明 结构的自引用结构体变量的定义和初始化结构体内存对齐为什么要内存对齐结构体传参结构体实现位段(位段的填充&可移植性)位段位段的内存分配空间如何开辟位段的跨平台问题位段的应用 枚举枚举类型的定义枚…...

人类程序员真要失业?首位“AI软件工程师”亮相引爆科技圈
初创公司Cognition成立不到两个月,但已经拥有十名天才工程师。他们推出了一款名为Devin的人工智能(AI)助手,可以协助人类软件工程师完成各种开发任务。Devin与现有的其他AI编码者不同,它能够从头开始构建网站、自动部署…...

redis的过期策略以及内存淘汰机制
redis采用的是定期删除惰性删除策略。 为什么不用定时删除策略? 定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要 将时间应用在处理请求,而不是删除key,因此没有采用这一策…...

华为数通方向HCIP-DataCom H12-821题库(多选题:161-180)
第161题 以下关于IPv6优势的描述,正确的是哪些项? A、底层自身携带安全特性 B、加入了对自动配置地址的支持,能够无状态自动配置地址 C、路由表相比IPv4会更大,寻址更加精确 D、头部格式灵活,具有多个扩展头 【参考答案】ABD 【答案解析】 第162题 在OSPF视图下使用Filt…...

网络通信与网络协议
网络编程是指利用计算机网络实现程序之间通信的一种编程方式。在网络编程中,程序需要通过网络协议(如 TCP/IP)来进行通信,以实现不同计算机之间的数据传输和共享。在网络编程中,通常有三个基本要素 IP 地址:定位网络中某台计算机端口号port:定…...

【矩阵】240. 搜索二维矩阵 II【中等】
搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1: 输入:matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22…...

详解uniapp的生命周期
这篇文章主要介绍了 uniapp 的生命周期, 应用生命周期是指应用程序从启动到关闭的整个过程,包括应用程序的启动、前后台切换、退出等, 需要的朋友可以参考下 Uniapp 作为一款跨平台应用开发框架,具有丰富的生命周期,以下是 Uniapp 的生命周期…...

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:PluginComponent)
提供外部应用组件嵌入式显示功能,即外部应用提供的UI可在本应用内显示。 说明: 该组件从API Version 9开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。本组件为系统接口。 子组件 无 接口 PluginComponent(value:…...

mysql笔记:15. 事务和锁
文章目录 一、事务概述二、事务基本操作三、事务保存点四、事务的隔离级别1. READ UNCOMMITTED设置事务的隔离级别 2. READ COMMITTED3. REPEATABLE READ4. SERIALIZABLE 五、MySQL的锁InnoDB的锁类型1. InnoDB的行级锁2. InnoDB的表级锁 死锁 在开发过程中,我们经常…...

Learn OpenGL 15 面剔除
面剔除 尝试在脑子中想象一个3D立方体,数数你从任意方向最多能同时看到几个面。如果你的想象力不是过于丰富了,你应该能得出最大的面数是3。你可以从任意位置和任意方向看向这个球体,但你永远不能看到3个以上的面。所以我们为什么要浪费时间…...

EndeavourOs(arch系)安装sunpinyin输入法(ibus) + 迅雷(xunlei-bin)
输入法 yay -S ibus yay -S ibus-libpinyin yay -S ibus-sunpinyin yay -Q ibus ibus-libpinyin ibus-sunpinyin #验证 # 注销然后打开ibus config... # 在Input Method 添加Chinese->SunPinYin # 使用Ctrl Space, 默认Super Space, 请自行修改 # 再次注销,开…...

Spring Cache框架的介绍和使用
Spring Cache spring cache是一个框架,实现类基于注解的缓存功能,只需要简单的加一个注解,就能实现缓存功能,大大简化我们在业务中操作缓存的代码。 spring cache只是提供了一层抽象,底层可以切换不同的cache实现&am…...

perl 用 XML::Parser 解析 XML文件,访问哈希
本篇我们会看到 Perl 成为知名编程语言的关键特色--哈希 hash(2000年以前叫:关联数组)。 在Perl 中,可以使用各种模块和函数来解析 XML元素和属性。其中,最古老的模块是 XML::Parser,它提供了一组完整的X…...

MATLAB中的矩阵和数组,它们之间有什么区别?
MATLAB中的矩阵和数组:概念、区别与联系 MATLAB(Matrix Laboratory,矩阵实验室)作为一款强大的数学软件,广泛应用于工程、科学、数学、计算机科学等领域。在MATLAB中,矩阵和数组是两个核心概念,…...

python爬虫实战——抖音
目录 1、分析主页作品列表标签结构 2、进入作品页前 判断作品是视频作品还是图文作品 3、进入视频作品页面,获取视频 4、进入图文作品页面,获取图片 5、完整参考代码 6、获取全部作品的一种方法 本文主要使用 selenium.webdriver(Firef…...