在 Langchain 中使用 RAPTOR 实现高级 RAG

news2024/11/26 13:51:44

RAPTOR:树结构的索引和检索系统的递归抽象处理-CSDN博客 

原文地址:implementing-advanced-rag-in-langchain-using-raptor

2024 年 3 月 24 日

RAPTOR 简介

递归抽象处理树组织检索(RAPTOR)是种全新而强大的索引和检索技术,它全面适用于LLM。该方法采用自下而上的策略,通过聚类和汇总文本片段(块)来构建一个分层的树状结构。

RAPTOR 论文介绍了一种创新的文档索引和检索策略:

  • 首先,将一组初始文档(单个文档的文本块或者完整文档)视为树的“叶子”。
  • 这些叶子被嵌入并表示为向量,然后通过聚类算法进行分组。
  • 接着,这些聚类被提炼为更高层次(更抽象)的信息,这些信息跨越相似的文档进行整合。
  • 整个过程递归进行,形成了一棵从原始文档(叶子)到更抽象摘要的“树”。

假设我们有来自于庞大手册的8个文档块,我们并不只是简单地对这些块进行嵌入并直接进行检索,而是首先将它们转换成向量形式,接着对这些高维向量进行降维处理。这样做是因为生成包含所有维度的簇会带来极高的计算成本,例如,在使用OpenAI的嵌入模型时,维度高达1536,而在使用常见的开源小型嵌入模型时,维度为384。

完成降维后,我们应用聚类算法对这些低维向量进行分组。接下来,我们收集每个聚类中的所有文档块,并对每个聚类的上下文进行总结。这样生成的摘要不仅反映了嵌入和聚类的结果,而且我们还重复这一过程,直到达到模型的令牌限制(即上下文窗口的大小)。

简而言之,RAPTOR方法的核心思想可以概括为:

  • 聚类并总结相似的文档块。
  • 将相关信息从相关文档中提取到摘要中。
  • 为那些只需要少量上下文就能回答的问题提供支持。

LLM 应用技术栈

  1. Langchain
  2. llm :zephyr-7b-beta.Q4_K_M.gguf
  3. 嵌入模型 — thenlper/gte-small
  4. clustering 算法: GMM (Gaussian Mixture Model)

代码实现

安装所需的库

!pip install -U langchain umap-learn scikit-learn langchain_community tiktoken langchain-openai langchainhub chromadb
!CMAKE_ARGS="-DLLAMA_CUBLAS=on" FORCE_CMAKE=1 pip install -qU llama-cpp-python

import locale
def getpreferredencoding(do_setlocale = True):
  return "UTF-8"
locale.getpreferredencoding = getpreferredencoding

获取 Zephyr 模型参数文件 

!wget "https://huggingface.co/TheBloke/zephyr-7B-beta-GGUF/resolve/main/zephyr-7b-beta.Q4_K_M.gguf"

实例化 LLM

from langchain_community.llms import LlamaCpp
from langchain_core.callbacks import CallbackManager, StreamingStdOutCallbackHandler
from langchain_core.prompts import PromptTemplate

# 放置在 GPU 上的层数,其余的将在 CPU 上进行,-1表示将所有层移至GPU。
n_gpu_layers = - 1  
# 介于 1 和 n_ctx 之间,考虑 GPU 中的 VRAM 量。
n_batch = 512  

# 回调支持 token-wise 流式处理
callback_manager = CallbackManager([StreamingStdOutCallbackHandler()])

model = LlamaCpp(
    model_path="/content/zephyr-7b-beta.Q4_K_M.gguf",
    n_gpu_layers=n_gpu_layers,
    n_batch=n_batch,
    temperature=0.75,
    max_tokens=1000,
    top_p=1,
    n_ctx=35000,
    callback_manager=callback_manager,
    verbose=True,  # Verbose is required to pass to the callback manager
)

实例化嵌入模型

from langchain.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores.utils import DistanceStrategy

EMBEDDING_MODEL_NAME = "thenlper/gte-small"

embd = HuggingFaceEmbeddings(
    model_name=EMBEDDING_MODEL_NAME,
    multi_process=True,
    model_kwargs={"device": "cuda"},
    encode_kwargs={"normalize_embeddings": True},  # set True for cosine similarity
)

加载数据

使用LangChain的LCEL文档作为输入数据

import matplotlib.pyplot as plt
import tiktoken
from bs4 import BeautifulSoup as Soup
from langchain_community.document_loaders.recursive_url_loader import RecursiveUrlLoader

# 用于统计每个文本中 Token 数量的辅助函数
def num_tokens_from_string(string: str, encoding_name: str) -> int:
    """返回文本字符串中的令牌数量"""
    encoding = tiktoken.get_encoding(encoding_name)
    num_tokens = len(encoding.encode(string))
    return num_tokens

# LCEL 文档
url = "https://python.langchain.com/docs/expression_language/"
loader = RecursiveUrlLoader(
    url=url, max_depth=20, extractor=lambda x: Soup(x, "html.parser").text
)
docs = loader.load()

# LCEL w/ PydanticOutputParser (主要 LCEL 文档之外)
url = "https://python.langchain.com/docs/modules/model_io/output_parsers/quick_start"
loader = RecursiveUrlLoader(
    url=url, max_depth=1, extractor=lambda x: Soup(x, "html.parser").text
)
docs_pydantic = loader.load()

# LCEL w/ Self Query (主要 LCEL 文档之外)
url = "https://python.langchain.com/docs/modules/data_connection/retrievers/self_query/"
loader = RecursiveUrlLoader(
    url=url, max_depth=1, extractor=lambda x: Soup(x, "html.parser").text
)
docs_sq = loader.load()

# 文档文本
docs.extend([*docs_pydantic, *docs_sq])
docs_texts = [d.page_content for d in docs]

通过计算每个文档的标记数量来检查我们的原始文档有多大,并使用直方图进行可视化

counts = [num_tokens_from_string(d, "cl100k_base") for d in docs_texts]

# 绘制标记计数的直方图
plt.figure(figsize=(10, 6))
plt.hist(counts, bins=30, color="blue", edgecolor="black", alpha=0.7)
plt.title("Histogram of Token Counts")
plt.xlabel("Token Count")
plt.ylabel("Frequency")
plt.grid(axis="y", alpha=0.75)

# 显示直方图
plt.show()

检查所有文档是否都适合我们文档的上下文窗口。

d_sorted = sorted(docs, key=lambda x: x.metadata["source"])
d_reversed = list(reversed(d_sorted))
concatenated_content = "\n\n\n --- \n\n\n".join(
    [doc.page_content for doc in d_reversed]
)
print(
    "Num tokens in all context: %s"
    % num_tokens_from_string(concatenated_content, "cl100k_base")
)

将文档分块以适合我们LLM的上下文窗口。

# 文档文本分割
from langchain_text_splitters import RecursiveCharacterTextSplitter

chunk_size_tok = 1000
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    chunk_size=chunk_size_tok, chunk_overlap=0
)
texts_split = text_splitter.split_text(concatenated_content)

print(f"Number of text splits generated: {len(texts_split)}")

 生成全局嵌入列表,它包含每个块的语义嵌入。

global_embeddings = [embd.embed_query(txt) for txt in texts_split]
print(len(global_embeddings[0])

通过将维度从 384 减少到 2 并可视化嵌入来生成减少的簇。

import matplotlib.pyplot as plt
from typing import Optional
import numpy as np
import umap

def reduce_cluster_embeddings(
    embeddings: np.ndarray,
    dim: int,
    n_neighbors: Optional[int] = None,
    metric: str = "cosine",
) -> np.ndarray:
    if n_neighbors is None:
        n_neighbors = int((len(embeddings) - 1) ** 0.5)
    return umap.UMAP(
        n_neighbors=n_neighbors, n_components=dim, metric=metric
    ).fit_transform(embeddings)


dim = 2
global_embeddings_reduced = reduce_cluster_embeddings(global_embeddings, dim)
print(global_embeddings_reduced[0])

plt.figure(figsize=(10, 8))
plt.scatter(global_embeddings_reduced[:, 0], global_embeddings_reduced[:, 1], alpha=0.5)
plt.title("Global Embeddings")
plt.xlabel("Dimension 1")
plt.ylabel("Dimension 2")
plt.show()

构建树

树构建过程中采用的聚类方法蕴含着几个引人入胜的创意。

GMM(高斯混合模型)

  • 对不同集群中数据点的分布进行建模
  • 通过评估模型的贝叶斯信息准则 (BIC) 来确定最佳簇数

UMAP(均匀流形逼近和投影)

  • 支持集群
  • 降低高维数据的维数
  • UMAP 有助于突出显示数据点基于相似性的自然分组

本地和全局集群

  • 用于分析不同尺度的数据
  • 有效捕获数据中的细粒度和更广泛的模式

阈值化

  • 在 GMM 上下文中应用以确定集群成员资格
  • 基于概率分布(将数据点分配给 ≥ 1 个簇)
import matplotlib.pyplot as plt
import numpy as np
from sklearn.mixture import GaussianMixture

def get_optimal_clusters(embeddings: np.ndarray, max_clusters: int = 50, random_state: int = 1234):
    max_clusters = min(max_clusters, len(embeddings))
    bics = [GaussianMixture(n_components=n, random_state=random_state).fit(embeddings).bic(embeddings)
            for n in range(1, max_clusters)]
    return np.argmin(bics) + 1

def gmm_clustering(embeddings: np.ndarray, threshold: float, random_state: int = 0):
    n_clusters = get_optimal_clusters(embeddings)
    gm = GaussianMixture(n_components=n_clusters, random_state=random_state).fit(embeddings)
    probs = gm.predict_proba(embeddings)
    labels = [np.where(prob > threshold)[0] for prob in probs]
    return labels, n_clusters

labels, _ = gmm_clustering(global_embeddings_reduced, threshold=0.5)

plot_labels = np.array([label[0] if len(label) > 0 else -1 for label in labels])
plt.figure(figsize=(10, 8))

unique_labels = np.unique(plot_labels)
colors = plt.cm.rainbow(np.linspace(0, 1, len(unique_labels)))

for label, color in zip(unique_labels, colors):
    mask = plot_labels == label
    plt.scatter(global_embeddings_reduced[mask, 0], global_embeddings_reduced[mask, 1], color=color, label=f'Cluster {label}', alpha=0.5)

plt.title("Cluster Visualization of Global Embeddings")
plt.xlabel("Dimension 1")
plt.ylabel("Dimension 2")
plt.legend()
plt.show()

创建数据框来检查与每个集群关联的文本。

import pandas as pd

simple_labels = [label[0] if len(label) > 0 else -1 for label in labels]

df = pd.DataFrame({
    'Text': texts_split,
    'Embedding': list(global_embeddings_reduced),
    'Cluster': simple_labels
})
print(df.head(3))

def format_cluster_texts(df):
    clustered_texts = {}
    for cluster in df['Cluster'].unique():
        cluster_texts = df[df['Cluster'] == cluster]['Text'].tolist()
        clustered_texts[cluster] = " --- ".join(cluster_texts)
    return clustered_texts
#
clustered_texts = format_cluster_texts(df)

定义 RAPTOR 辅助函数

from typing import Dict, List, Optional, Tuple

import numpy as np
import pandas as pd
import umap
from langchain.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from sklearn.mixture import GaussianMixture

RANDOM_SEED = 224  # Fixed seed for reproducibility


def global_cluster_embeddings(
        embeddings: np.ndarray,
        dim: int,
        n_neighbors: Optional[int] = None,
        metric: str = "cosine",
) -> np.ndarray:
    """
    使用 UMAP 对嵌入执行全局降维。

    参数:
    - embeddings:作为 numpy 数组的输入嵌入。
    - dim:缩减空间的目标维度。
    - n_neighbors:可选;数字每个点要考虑的邻居数。如果未提供,则默认为嵌入数量的平方根。
    - metric:用于 UMAP 的距离度量。

    返回:
    - 减少到指定维度的嵌入的 numpy 数组。
    """
    if n_neighbors is None:
        n_neighbors = int((len(embeddings) - 1) ** 0.5)
    return umap.UMAP(
        n_neighbors=n_neighbors, n_components=dim, metric=metric
    ).fit_transform(embeddings)


def local_cluster_embeddings(
        embeddings: np.ndarray, dim: int, num_neighbors: int = 10, metric: str = "cosine"
) -> np.ndarray:
    """
    使用 UMAP 对嵌入执行局部降维,通常在全局聚类之后。

    参数:
    - embeddings:作为 numpy 数组的输入嵌入。
    - dim:缩减空间的目标维度。
    - num_neighbors:每个点要考虑的邻居数量。
    - metric:用于 UMAP 的距离度量。

    返回:
    - 减少到指定维度的嵌入的 numpy 数组。
    """
    return umap.UMAP(
        n_neighbors=num_neighbors, n_components=dim, metric=metric
    ).fit_transform(embeddings)


def get_optimal_clusters(
        embeddings: np.ndarray, max_clusters: int = 50, random_state: int = RANDOM_SEED
) -> int:
    """
    使用贝叶斯信息准则 (BIC) 和高斯混合模型确定最佳簇数。

    参数:
    - embeddings:作为 numpy 数组的输入嵌入。
    - max_clusters:要考虑的最大簇数。
    - random_state:可重复性的种子。

    返回:
    - 表示找到的最佳簇数的整数。
    """
    max_clusters = min(max_clusters, len(embeddings))
    n_clusters = np.arange(1, max_clusters)
    bics = []
    for n in n_clusters:
        gm = GaussianMixture(n_components=n, random_state=random_state)
        gm.fit(embeddings)
        bics.append(gm.bic(embeddings))
    return n_clusters[np.argmin(bics)]


def GMM_cluster(embeddings: np.ndarray, threshold: float, random_state: int = 0):
    """
    使用高斯聚类嵌入基于概率阈值的混合模型 (GMM)。

    参数:
    - embeddings:作为 numpy 数组的输入嵌入。
    - 阈值:将嵌入分配给簇的概率阈值。
    - random_state:可重复性的种子。

    返回:
    - 包含聚类标签和确定的聚类数量的元组。
    """
    n_clusters = get_optimal_clusters(embeddings)
    gm = GaussianMixture(n_components=n_clusters, random_state=random_state)
    gm.fit(embeddings)
    probs = gm.predict_proba(embeddings)
    labels = [np.where(prob > threshold)[0] for prob in probs]
    return labels, n_clusters


def perform_clustering(
        embeddings: np.ndarray,
        dim: int,
        threshold: float,
) -> List[np.ndarray]:
    """
    通过首先全局降低嵌入的维数,然后使用高斯混合模型进行聚类,最后对嵌入进行聚类在每个全局聚类中执行局部聚类。

    参数:
    - embeddings:作为 numpy 数组的输入嵌入。
    - dim:UMAP 缩减的目标维数。
    - Threshold:在 GMM 中将嵌入分配给聚类的概率阈值。

    返回:
    - numpy 数组列表,其中每个数组包含每个嵌入的簇 ID。
    """
    if len(embeddings) <= dim + 1:
        # Avoid clustering when there's insufficient data
        return [np.array([0]) for _ in range(len(embeddings))]

    # Global dimensionality reduction
    reduced_embeddings_global = global_cluster_embeddings(embeddings, dim)
    # Global clustering
    global_clusters, n_global_clusters = GMM_cluster(
        reduced_embeddings_global, threshold
    )

    all_local_clusters = [np.array([]) for _ in range(len(embeddings))]
    total_clusters = 0

    # Iterate through each global cluster to perform local clustering
    for i in range(n_global_clusters):
        # Extract embeddings belonging to the current global cluster
        global_cluster_embeddings_ = embeddings[
            np.array([i in gc for gc in global_clusters])
        ]

        if len(global_cluster_embeddings_) == 0:
            continue
        if len(global_cluster_embeddings_) <= dim + 1:
            # Handle small clusters with direct assignment
            local_clusters = [np.array([0]) for _ in global_cluster_embeddings_]
            n_local_clusters = 1
        else:
            # Local dimensionality reduction and clustering
            reduced_embeddings_local = local_cluster_embeddings(
                global_cluster_embeddings_, dim
            )
            local_clusters, n_local_clusters = GMM_cluster(
                reduced_embeddings_local, threshold
            )

        # Assign local cluster IDs, adjusting for total clusters already processed
        for j in range(n_local_clusters):
            local_cluster_embeddings_ = global_cluster_embeddings_[
                np.array([j in lc for lc in local_clusters])
            ]
            indices = np.where(
                (embeddings == local_cluster_embeddings_[:, None]).all(-1)
            )[1]
            for idx in indices:
                all_local_clusters[idx] = np.append(
                    all_local_clusters[idx], j + total_clusters
                )

        total_clusters += n_local_clusters

    return all_local_clusters


def embed(texts):
    """
    为文本文档列表生成嵌入。

    此函数假设存在 `embd` 对象,其方法 `embed_documents`
    接受文本列表并返回其嵌入。

    参数:
    - texts:List[str],要嵌入的文本文档列表。

    返回:
    - numpy.ndarray:嵌入数组
    """
    text_embeddings = embd.embed_documents(texts)
    text_embeddings_np = np.array(text_embeddings)
    return text_embeddings_np


def embed_cluster_texts(texts):
    """
    嵌入文本列表并对它们进行聚类,返回包含文本的DataFrame 、它们的嵌入和聚类标签。
    该函数将嵌入生成和聚类合并为一个步骤。它假设存在先前定义的“perform_clustering”函数,该函数对嵌入执行聚类。

    参数:
        - texts:List[str],要处理的文本文档列表。

    返回:
        - pandas.DataFrame:包含原始文本、其嵌入以及分配的簇标签的 DataFrame。
    """
    text_embeddings_np = embed(texts)  # Generate embeddings
    cluster_labels = perform_clustering(
        text_embeddings_np, 10, 0.1
    )  # Perform clustering on the embeddings
    df = pd.DataFrame()  # Initialize a DataFrame to store the results
    df["text"] = texts  # Store original texts
    df["embd"] = list(text_embeddings_np)  # Store embeddings as a list in the DataFrame
    df["cluster"] = cluster_labels  # Store cluster labels
    return df


def fmt_txt(df: pd.DataFrame) -> str:
    """
    将 DataFrame 中的文本文档格式化为单个字符串.

    参数:
    - df:包含要格式化的文本文档的 'text' 列的 DataFrame。

    返回:
    - 所有文本文档均由特定分隔符连接的单个字符串。
    """
    unique_txt = df["text"].tolist()
    return "--- --- \n --- --- ".join(unique_txt)


def embed_cluster_summarize_texts(
        texts: List[str], level: int
) -> Tuple[pd.DataFrame, pd.DataFrame]:
    """
    嵌入、聚类和总结文本列表。此函数首先生成文本的嵌入,
    根据相似性对它们进行聚类,扩展聚类分配以便于处理,然后总结每个聚类内的内容。

    参数:
    - texts:要处理的文本文档列表。
    - level:一个整数参数,可以定义处理的深度或细节。

    返回:
    - 包含两个 DataFrame 的元组:
      1. 第一个 DataFrame (`df_clusters`) 包含原始文本、它们的嵌入和聚类分配。
      2. 第二个 DataFrame (`df_summary`) 包含每个集群的摘要、指定的详细级别以及集群标识符。
    """

    # Embed and cluster the texts, resulting in a DataFrame with 'text', 'embd', and 'cluster' columns
    df_clusters = embed_cluster_texts(texts)

    # Prepare to expand the DataFrame for easier manipulation of clusters
    expanded_list = []

    # Expand DataFrame entries to document-cluster pairings for straightforward processing
    for index, row in df_clusters.iterrows():
        for cluster in row["cluster"]:
            expanded_list.append(
                {"text": row["text"], "embd": row["embd"], "cluster": cluster}
            )

    # Create a new DataFrame from the expanded list
    expanded_df = pd.DataFrame(expanded_list)

    # Retrieve unique cluster identifiers for processing
    all_clusters = expanded_df["cluster"].unique()

    print(f"--Generated {len(all_clusters)} clusters--")

    # Summarization
    template = """Here is a sub-set of LangChain Expression Langauge doc.

    LangChain Expression Langauge provides a way to compose chain in LangChain.

    Give a detailed summary of the documentation provided.

    Documentation:
    {context}
    """
    prompt = ChatPromptTemplate.from_template(template)
    chain = prompt | model | StrOutputParser()

    # Format text within each cluster for summarization
    summaries = []
    for i in all_clusters:
        df_cluster = expanded_df[expanded_df["cluster"] == i]
        formatted_txt = fmt_txt(df_cluster)
        summaries.append(chain.invoke({"context": formatted_txt}))

    # Create a DataFrame to store summaries with their corresponding cluster and level
    df_summary = pd.DataFrame(
        {
            "summaries": summaries,
            "level": [level] * len(summaries),
            "cluster": list(all_clusters),
        }
    )

    return df_clusters, df_summary


def recursive_embed_cluster_summarize(
        texts: List[str], level: int = 1, n_levels: int = 3
) -> Dict[int, Tuple[pd.DataFrame, pd.DataFrame]]:
    """
    递归地嵌入、聚类和汇总文本,直到指定级别或直到唯一聚类的数量变为 1,存储每个级别的结果。

    参数:
    - texts:List[str],要处理的文本。
    - level:int,当前递归级别(从1开始)。
    - n_levels:int,最大递归深度。

    返回:
    - Dict[int, Tuple[pd.DataFrame, pd.DataFrame]],一个字典,其中键是递归级别,值是包含该级别的簇 DataFrame 和摘要 DataFrame 的元组。
    """
    results = {}  # Dictionary to store results at each level

    # Perform embedding, clustering, and summarization for the current level
    df_clusters, df_summary = embed_cluster_summarize_texts(texts, level)

    # Store the results of the current level
    results[level] = (df_clusters, df_summary)

    # Determine if further recursion is possible and meaningful
    unique_clusters = df_summary["cluster"].nunique()
    if level < n_levels and unique_clusters > 1:
        # Use summaries as the input texts for the next level of recursion
        new_texts = df_summary["summaries"].tolist()
        next_level_results = recursive_embed_cluster_summarize(
            new_texts, level + 1, n_levels
        )

        # Merge the results from the next level into the current results dictionary
        results.update(next_level_results)

    return results

构建树

leaf_texts = docs_texts
results = recursive_embed_cluster_summarize(leaf_texts, level=1, n_levels=3)

 生成最终摘要

此步骤很大程度上取决于我们如何从向量存储中检索文档。基本上有两种选择。

  1. 树遍历检索

树遍历从树的根级别开始,并根据向量嵌入的余弦相似度检索节点的前 k 个文档。因此,在每个级别,它都会从子节点检索前 k 个文档。

2. 倒塌树检索

折叠树检索是一种更简单的方法。它将所有树折叠成单层并检索节点,直到基于查询向量的余弦相似度达到令牌的阈值数量。

在我们的代码中,我们将提取数据框文本、集群文本、最终摘要文本,并将它们组合起来创建一个包含根文档和摘要的大型文本列表。然后将该文本存储到矢量存储中。

# Initialize all_texts with leaf_texts
all_texts = leaf_texts.copy()

# Iterate through the results to extract summaries from each level and add them to all_texts
for level in sorted(results.keys()):
    # Extract summaries from the current level's DataFrame
    summaries = results[level][1]["summaries"].tolist()
    # Extend all_texts with the summaries from the current level
    all_texts.extend(summaries)
#Final Summaries extracted
print(all_texts)

将文本加载到矢量存储中:构建索引

vectorstore = Chroma.from_texts(texts=all_texts, embedding=embd) 
retriever = vectorstore.as_retriever()

构建查询引擎

from langchain import hub
from langchain_core.runnables import RunnablePassthrough

# Prompt
prompt = hub.pull("rlm/rag-prompt")


# Post-processing
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)


# Chain
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)

Langchain Hub RAG Prompt

print(prompt)
print(prompt.messages[0].prompt.template)

Ask Query

response =rag_chain.invoke("What is LCEL?")
print(str(response))

response =rag_chain.invoke("How to define a RAG chain? Give me a specific code example.")

Conclusion

在这里,我们使用了先进的检索技术RAPTOR来实现长上下文中的基于检索的生成(RAG)。

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

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

相关文章

基于springboot的实习生管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

SV学习笔记(二)

接口 什么是接口&#xff1f; 接口 主要用作验证 &#xff0c;国外有些团队会使用sv进行设计&#xff0c;那么接口就会用作设计。验证环境中&#xff0c;接口可以 使连接变得简洁而不易出错 。interface和module的使用性质很像&#xff0c; 可以定义端口&#xff0c;也可以定…

【单片机家电产品--晶闸管】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 单片机家电产品–晶闸管 前言 记录学习单片机家电产品内容 已转载记录为主 一、知识点 晶体管和晶闸管之间的区别 晶体管和晶闸管之间的区别 什么是可控硅&#xff08;…

观察和配置MAC地址表

目录 原理概述 实验目的 实验内容 实验拓扑 ​编辑1&#xff0e;基本配置 2.观察正常状态时的MAC地址表 4.配置静态MAC地址表项 原理概述 MAC 地址表是交换机的一个核心组成部分&#xff0c;交换机主要是根据 MAC 地址表来进行帧的转发的。交换机对帧的转发操作行为一共有…

Linux 命令 top 详解

1 top命令介绍 Linux系统中&#xff0c;Top命令主要用于实时运行系统的监控&#xff0c;包括Linux内核管理的进程或者线程的资源占用情况。这个命令对所有正在运行的进程和系统负荷提供不断更新的概览信息&#xff0c;包括系统负载、CPU利用分布情况、内存使用、每个进程的内容…

Golang Context是什么

一、这篇文章我们简要讨论Golang的Context有什么用 1、首先说一下Context的基本作用&#xff0c;然后在讨论他的实现 (1)数据传递&#xff0c;子Context只能看到自己的和父Context的数据&#xff0c;子Context是不能看到孙Context添加的数据。 (2)父子协程的协同&#xff0c;比…

c++的学习之路:9、STL简介与string(1)

一、STL 1、什么是STL STL(standard template libaray-标准模板库)&#xff1a;是C标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 也就是说STL就是一个模板&#xff0c;这个模板就是整合了很多库让我们方…

磁盘如何分配数据数据

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;算法分析与设计 ⛺️稳中求进&#xff0c;晒太阳 磁盘如何分配数据 数据切割&#xff1a; 按照固定长度进行切割---》编码翻译&#xff08;常用&#xff09; 计算机要求按照8bit(字节)进…

【蓝桥杯练习】tarjan算法求解LCA

还是一道比较明显的求LCA(最近公共祖先)模型的题目,我们可以使用多种方法来解决该问题&#xff0c;这里我们使用更好写的离线的tarjan算法来解决该问题。 除去tarjan算法必用的基础数组&#xff0c;我们还有一个数组d[],d[i]记录的是每个点的出度&#xff0c;也就是它的延迟时间…

学习Python第十五天:第一个程序python程序

第一个程序&#xff1a;ZIP文件口令破解机 编写ZIP文件口令破解机要从学习zipfile库的使用方法着手&#xff0c;打开pythn解释器&#xff0c;我们用help(zipfile)命令进一步了解这个库&#xff0c;并重点看一下zipfile类中的extractall()方法&#xff0c;这个类和这个方法对我…

在仿真环境中运行lio-sam

文章目录 前言LIO-SAM环境编译运行键盘控制编译lio-sam遇到的问题前言 Gazebo 仿真提供了一个高效且成本低廉的平台,使研究人员和开发者能够在安全且可控的虚拟环境中设计、测试和优化机器人系统。它允许快速原型制作和迭代,精确控制测试条件,并能模拟复杂或危险的场景,从…

2013年认证杯SPSSPRO杯数学建模B题(第二阶段)流行音乐发展简史全过程文档及程序

2013年认证杯SPSSPRO杯数学建模 B题 流行音乐发展简史 原题再现&#xff1a; 随着互联网的发展&#xff0c;流行音乐的主要传播媒介从传统的电台和唱片逐渐过渡到网络下载和网络电台等。网络电台需要根据收听者的已知喜好&#xff0c;自动推荐并播放其它音乐。由于每个人喜好…

【解决】Unity Profile | FindMainCamera

开发平台&#xff1a;Unity 2020.3.7f1c1 关键词&#xff1a;FindMainCamera   问题背景 ModelViewer 是开发者基于 UnityEngine 编写的相机控制组件。ModelView.Update 中调度52次并触发3次GC.Collect。显然并不期望并尽可能避免 Update 造成的GC 问题。事实上 FindMainCame…

C语言第三十九弹---预处理(上)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 预处理 1、预定义符号 2、#define定义常量 3、#define定义宏 4、带有副作用的宏参数 5、宏替换的规则 6、宏和函数的对比 总结 在C语言中&#xff0c;预处…

经久耐用耐强腐蚀PFA材质气体洗涤瓶全氟烷氧基树脂尾气吸收瓶

PFA洗气瓶是一种常用于净化和干燥各种气体的实验室器皿&#xff0c;以去除其中的水分、油脂、颗粒物等杂质&#xff0c;从而使需要用到的气体满足实验要求。 PFA气体吸收瓶 PFA洗气瓶的工作原理&#xff1a; 主要是通过液体吸收、溶解或发生化学反应来去除气体中的杂质。在洗气…

LeetCode题练习与总结:螺旋矩阵

一、题目描述 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]示例 2&#xff1a; 输入&#xff1a;ma…

物联网行业趋势——青创智通

工业物联网解决方案-工业IOT-青创智通 随着科技的不断进步和应用场景的日益扩大&#xff0c;物联网行业呈现出迅猛发展的势头。作为当今世界最具前瞻性和战略意义的领域之一&#xff0c;物联网行业的趋势和未来发展值得深入探讨。 ​一、物联网行业正逐渐实现全面普及。随着物…

matlab使用教程(32)—求解偏微分方程(3)

1求解 PDE 方程组 此示例说明由两个偏微分方程构成的方程组的解的构成&#xff0c;以及如何对解进行计算和绘图。 以如下 PDE 方程组为例 要在 MATLAB 中求解该方程&#xff0c;您需要对方程、初始条件和边界条件编写代码&#xff0c;然后在调用求解器pdepe 之前选择合适的解…

“帮助“Java成长的世界级大师不简单!

文章目录 初探编程&#xff1a;“天啊&#xff0c;真酷&#xff0c;程序真的能学习。”哺育Java成长&#xff0c;成为Java幕后英雄出书《Effective Java》斩获Jolt图书大奖 是谁&#xff1f;作品一出版就获得著名的Jolt图书大奖&#xff0c;每一版本豆瓣评分均超9.0&#xff01…