大模型高级 RAG 检索策略之混合检索

news2024/11/17 16:32:30

古人云:兼听则明,偏信则暗,意思是要同时听取各方面的意见,才能正确认识事物,只相信单方面的话,必然会犯片面性的错误。

在 RAG(Retrieval Augmented Generation)应用中也是如此,如果我们可以同时从多个信息源中获取信息,那么我们的检索结果会更加全面和准确。今天我们就来介绍高级 RAG 检索策略中的混合检索,并在实际操作中结合 ElaticSearch 和 Llama3 来实现混合检索的效果。

原理介绍

混合检索也叫融合检索,也叫多路召回,是指在检索过程中,同时使用多种检索方式,然后将多种检索结果进行融合,得到最终的检索结果。混合检索的优势在于可以充分利用多种检索方式的优势,弥补各种检索方式的不足,从而提高检索的准确性和效率,下面是混合检索的流程图:

图片

  • 首先是问题查询,这一过程的设计可以简单也可以复杂,简单的做法是直接将原始查询传递给检索器,而复杂一点的做法是通过 LLM(大语言模型)为原始查询生成子查询或相似查询,然后再将生成后的查询传递给检索器

  • 然后是检索器执行检索,检索可以在同一数据源上进行不同维度的检索,比如向量检索和关键字检索,也可以是在不同数据源上进行检索,比如文档和数据库

  • 检索过程从原来一个问题变成了多个问题检索,如果串行执行这些检索,那么检索的效率会大大降低,所以我们需要并行执行多个检索,这样才可以保证检索的效率

  • 最后是融合检索结果,在这一过程中,我们需要对检索结果进行去重,因为在检索的多个结果中,有些结果可能是重复的,同时我们还需要对检索结果进行排序,排序方法一般采用 RRF(倒数排名融合),选出最匹配的检索结果

节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。

技术交流

前沿技术资讯、算法交流、求职内推、算法竞赛、面试交流(校招、社招、实习)等、与 10000+来自港科大、北大、清华、中科院、CMU、腾讯、百度等名校名企开发者互动交流~

我们建了算法岗面试与技术交流群, 想要进交流群、需要源码&资料、提升技术的同学,可以直接加微信号:mlc2040。加的时候备注一下:研究方向 +学校/公司+CSDN,即可。然后就可以拉你进群了。

方式①、微信搜索公众号:机器学习社区,后台回复:技术交流
方式②、添加微信号:mlc2040,备注:技术交流+CSDN

环境准备

为了更好地了解混合检索的原理和实现,今天我们将通过 LLM 应用框架LlamaIndex[1],结合 Meta 最新开源的模型Llama3[2]和开源搜索引擎ElasticSearch[3],来实现一个高效的混合检索系统。在 RAG 检索过程中除了需要用到 LLM 的模型外,还需要用到 Embedding 模型和 Rerank 模型,这些模型我们也统一使用本地部署的模型,这样可以更好地了解各种模型的使用和部署。

LlamaIndex 集成 Llama3

首先是进行 Llama3 的本地化部署,有多种工具可以部署 Llama3,比如 Ollama[4] 或 vllm[5],而且这些工具都提供了兼容 OpenAI 的 API 接口,vllm 的部署方式可以参考我之前的文章

部署完成后,我们再看如何在 LlamaIndex 中集成 Llama3。虽然 LlamaIndex 提供了自定义 LLM[7]的功能,但继承自CustomeLLM类来实现自定义 LLM 的方式比较复杂,需要从头实现completechat等方法。这里推荐 LlamaInex 另外一个创建自定义 LLM 的方法,即使用OpenAILike类,这个类是对 OpenAI 类进行轻量级封装,只要有兼容 OpenAI 的 API 服务,就可以直接使用该类来获得 OpenAI LLM 的功能。

要使用OpenAILike类,首先需要安装相关依赖包pip install llama-index-llms-openai-like,然后使用以下代码进行集成:

from llama_index.llms.openai_like import OpenAILike
from llama_index.core.base.llms.types import ChatMessage, MessageRole
from llama_index.core import PromptTemplate

llm = OpenAILike(
    model="llama3",
    api_base="you-local-llama3-api",
    api_key="fake_key",
    is_chat_model=True,
)
prompt_str = "Please generate related movies to {movie_name}"
prompt_tmpl = PromptTemplate(prompt_str)
response = llm.chat(
    [
        ChatMessage(
            role=MessageRole.SYSTEM,
            content="You are a helpful assistant.",
        ),
        ChatMessage(
            role=MessageRole.USER,
            content=prompt_tmpl.format(movie_name="Avengers"),
        ),
    ]
)
print(f"response: {response}")

# 显示结果
response: assistant: Here are some movie recommendations that are similar to the Avengers franchise:

1. **Guardians of the Galaxy** (2014) - Another Marvel superhero team-up film, with a fun and quirky tone.
2. **The Justice League** (2017) - A DC Comics adaptation featuring iconic superheroes like Superman, Batman, Wonder Woman, and more.
......
  • OpenAILike对象中,参数model为模型名称,api_base为本地 Llama3 的 API 服务地址

  • api_key可以随便填写,但不能不传这个参数,否则会出现连接超时的错误

  • is_chat_model为是否是 chat 模型,因为 OpenAI 的模型分为 chat 模型和非 chat 模型

  • 然后我们使用 LLM 对象进行了一个普通的对话,结果可以正常返回

LlamaIndex 集成 ElasticSearch

在 RAG 应用中向量数据库是必不可少的一项功能,而 Elasticsearch 能够存储各种类型的数据,包括结构化和非结构化数据,并且支持全文检索和向量检索。

部署完 ElasticSearch 后,还需要安装 LlamaIndex 的 Elasticsearch 依赖包pip install llama-index-vector-stores-elasticsearch,然后使用以下代码示例就可以集成 ElasticSearch:

from llama_index.vector_stores.elasticsearch import ElasticsearchStore

es = ElasticsearchStore(
    index_name="my_index",
    es_url="http://localhost:9200",
)
  • index_name 是 ElasticSearch 的索引名称,es_url 是 ElasticSearch 服务的地址

自定义 Embedding 和 Rerank 模型

在高级 RAG 的检索过程中,需要用到 Embedding 模型来对文档和问题进行向量化,然后使用 Rerank 模型对检索结果进行重排序。同样有很多工具可以部署这 2 种模型,比如TEI[9] 和 Xinference[10]等。

Embedding 模型的启动命令如下,这里我们使用了BAAI/bge-base-en-v1.5[12]这个 Embeddings 模型,服务端口为 6006:

text-embeddings-router --model-id BAAI/bge-base-en-v1.5 --revision refs/pr/4 --port 6006

Rerank 模型的启动命令如下,这里我们使用了BAAI/bge-reranker-base[13]这个 Rerank 模型,服务端口为 7007:

text-embeddings-router --model-id BAAI/bge-reranker-base --revision refs/pr/4 --port 7007

多种检索方式

数据入库

在介绍检索之前,我们先来了解下 LlamaIndex 如何使用 ElasticSearch 对文档进行解析和入库,这里的测试文档还是用维基百科上的复仇者联盟[14]电影剧情,示例代码如下:

from llama_index.vector_stores.elasticsearch import ElasticsearchStore
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, StorageContext
from llama_index.core.node_parser import SentenceSplitter
from llms import CustomEmbeddings

store = ElasticsearchStore(
    index_name="avengers",
    es_url="http://localhost:9200",
)
documents = SimpleDirectoryReader("./data").load_data()
node_parser = SentenceSplitter(chunk_size=256, chunk_overlap=50)
storage_context = StorageContext.from_defaults(vector_store=store)
embed_model = CustomEmbeddings(
    model="BAAI/bge-base-en-v1.5", url="http://localhost:6006"
)
VectorStoreIndex.from_documents(
    documents,
    transformations=[node_parser],
    embed_model=embed_model,
    storage_context=storage_context,
)
  • 首先定义了一个 ElasticsearchStore 对象来连接 ElaticSearch 本地服务

  • 然后使用 SimpleDirectoryReader 加载本地的文档数据

  • 使用 SentenceSplitter 对文档进行分块处理,应为 TEI 的输入 Token 数最大只能 512,所以这里的 chunk_size 设置为 256,chunk_overlap 设置为 50

  • 构建 StorageContext 对象,指定向量存储为之前定义的 ElasticsearchStore 对象

  • 创建一个自定义 Embeddings 对象,使用的是 TEI 部署的 Embeddings 模型服务,

  • 最后使用 VectorStoreIndex 对象将文档数据入库

当执行完代码后,可以在 ElasticSearch 的avengers索引中看到文档数据,如下图所示:

图片

全文检索

数据入库后,我们再来看下如何在 LlamaIndex 中使用 Elasticsearch 进行全文检索。

全文检索是 Elasticsearch 的基本功能,有时候也叫关键字检索,是指根据关键字在文档中进行检索,支持精确匹配,同时高级功能也支持模糊匹配、同义词替换、近义词搜索等。在 LlamaIndex 中使用 Elasticsearch 进行全文检索的代码如下:

from llama_index.vector_stores.elasticsearch import AsyncBM25Strategy
from llama_index.core import Settings

text_store = ElasticsearchStore(
    index_name="avengers",
    es_url="http://localhost:9200",
    retrieval_strategy=AsyncBM25Strategy(),
)
Settings.embed_model = embed_model
text_index = VectorStoreIndex.from_vector_store(
    vector_store=text_store,
)
text_retriever = text_index.as_retriever(similarity_top_k=2)
  • 这里重新定义了一个 ElasticsearchStore 对象,但这次指定了检索策略为 BM25,如果要使用全文检索则必须指定这个检索策略

  • ElasticsearchStore对象作为参数来创建VectorStoreIndex 对象

  • 最后通过VectorStoreIndex对象创建全文检索的检索器,这里设置检索结果的数量为 2

BM25 是一种在信息检索领域广泛采用的排名函数,主要用于评估文档与用户查询的相关性。该算法的基本原理是将用户查询(query)分解为若干语素(qi),然后计算每个语素与搜索结果之间(document D)的相关性。通过累加这些相关性得分,BM25 最终得出查询与特定文档之间的总相关性评分。这种检索策略在现代搜索引擎中非常常见。

向量检索

我们再来了解 LlamaIndex 中如何使用 Elasticsearch 进行向量检索。

向量检索是一种基于机器学习的信息检索技术,它使用数学向量来表示文档和查询。在 LlamaIndex 中使用 Elasticsearch 进行向量检索有 2 种检索策略,分别是DenseSparse,这两种策略的区别在于向量的稠密度,Dense检索的号码每一位都是有用的数字,就像一个充满数字的电话号码,而Sparse检索的号码大部分都是零,只有少数几个位置有数字,就像一个电话号码大部分是零,只有几个位置有数字。如果需要更精细、更复杂的检索方法,用Dense检索,如果需要简单快速的方法,用Sparse检索。ElasicsearchStore类默认的检索策略是Dense,下面是向量检索的代码示例:

from llama_index.vector_stores.elasticsearch import AsyncDenseVectorStrategy, AsyncSparseVectorStrategy

vector_store = ElasticsearchStore(
    index_name="avengers",
    es_url="http://localhost:9200",
    retrieval_strategy=AsyncDenseVectorStrategy(),
    # retrieval_strategy=AsyncSparseVectorStrategy(model_id=".elser_model_2"),
)
Settings.embed_model = embed_model
vector_index = VectorStoreIndex.from_vector_store(
    vector_store=vector_store,
)
vector_retriever = vector_index.as_retriever(similarity_top_k=2)
  • 向量检索的代码和全文检索的代码类似

  • 如果是使用Dense检索策略,可以指定retrieval_strategy=AsyncDenseVectorStrategy(),也可以不指定retrieval_strategy参数

  • 如果是使用Sparse检索策略,需要指定retrieval_strategy=AsyncSparseVectorStrategy(model_id=".elser_model_2"),这里需要额外部署 ElasticSearch 的 ELSER 模型[16]

混合检索

定义好了 2 种检索器后,我们再来了解如何将这些检索进行融合,在 LlamaIndex 的 ElasticsearchStore 类中提供了混合检索的方法,示例代码如下:

from llama_index.vector_stores.elasticsearch import AsyncDenseVectorStrategy

vector_store = ElasticsearchStore(
    index_name="avengers",
    es_url="http://localhost:9200",
    retrieval_strategy=AsyncDenseVectorStrategy(hybrid=True),
)
  • 这里的检索策略还是使用Dense检索策略,但是指定了hybrid=True参数,表示使用混合检索

设置了混合检索策略后,在融合检索结果时会自动使用 Elasicsearch 的 RRF 功能。

RRF(倒数排名融合) 是一种融合检索算法,用于结合多个检索结果列表。每个结果列表中的每个文档被分配一个分数,分数基于文档在列表中的排名位置。该算法的基本思想是,通过对多个检索器的结果进行融合,来提高检索性能。

但在 Elasticsearch 的免费版本中,这个功能是不可用的:

图片

因此我们需要自己实现 RRF 功能,RRF 的论文可以看这里[17],下面是 RRF 的代码实现:

from typing import List
from llama_index.core.schema import NodeWithScore

def fuse_results(results_dict, similarity_top_k: int = 2):
    """Fuse results."""
    k = 60.0
    fused_scores = {}
    text_to_node = {}

    # 计算倒数排名分数
    for nodes_with_scores in results_dict.values():
        for rank, node_with_score in enumerate(
            sorted(
                nodes_with_scores, key=lambda x: x.score or 0.0, reverse=True
            )
        ):
            text = node_with_score.node.get_content()
            text_to_node[text] = node_with_score
            if text not in fused_scores:
                fused_scores[text] = 0.0
            fused_scores[text] += 1.0 / (rank + k)

    # 结果按分数排序
    reranked_results = dict(
        sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)
    )

    # 结果还原为节点集合
    reranked_nodes: List[NodeWithScore] = []
    for text, score in reranked_results.items():
        reranked_nodes.append(text_to_node[text])
        reranked_nodes[-1].score = score

    return reranked_nodes[:similarity_top_k]
  • 方法的参数results_dict是所有检索器的检索结果集合,similarity_top_k是最相似的结果数量

  • 假设results_dict的值是{'full-text': [nodes], 'vector': [nodes]},这个方法方法的作用是将所有的检索结果节点进行融合,然后选出最相似的similarity_top_k个节点

  • 方法开头是初始化一些变量,k 用于计算倒数排名分数,fused_scores 用于存储节点文本和融合后分数的映射,text_to_node 用于存储节点文本到节点的映射

  • 然后是计算每个节点的倒数排名分数,先将 results_dict 中的每个节点按照分数进行排序,然后计算每个节点的倒数排名分数,将结果保存到 fused_scores 中,同时将节点文本和节点的关系保存到 text_to_nodes

  • 接着再对 fused_scores 按照倒数排名分数进行排序,得到 reranked_results

  • 然后根据 reranked_results 将结果还原成节点集合的形式,并将节点的分数设置为融合后的分数,最终结果保存到 reranked_nodes 列表中

  • 最后返回最相似的结果,返回 reranked_nodes 列表中的前 similarity_top_k 个节点

定义好融合函数后,我们再定义一个方法来执行多个检索器,这个方法返回的结果就是融合函数的参数 results_dict,示例代码如下:

from tqdm.asyncio import tqdm

def run_queries(query, retrievers):
    """Run query against retrievers."""
    tasks = []
    for i, retriever in enumerate(retrievers):
        tasks.append(retriever.aretrieve(query))

    task_results = await tqdm.gather(*tasks)

    results_dict = {}
    for i, query_result in enumerate(task_results):
        results_dict[(query, i)] = query_result

    return results_dict
  • 方法的参数query是原始问题,retrievers是多个检索器的集合

  • 将问题传给每个检索器,构建异步任务列表tasks

  • 然后使用await tqdm.gather(*tasks)并行执行所有的检索器,并行执行可以提高检索效率

  • 最后将检索结果保存到results_dict中,返回results_dict

因为我们使用了异步方式进行检索,原先的CustomEmbeddings中的方法也需要修改,示例代码如下:

+import asyncio

-    def _aget_query_embedding(self, query: str) -> Embedding:
-        return get_embedding(text=query, model=self._model, url=self._url)
+    async def _aget_query_embedding(self, query: str) -> Embedding:
+        loop = asyncio.get_event_loop()
+        return await loop.run_in_executor(
+            None, get_embedding, query, self._model, self._url
+        )

然后我们构建一个融合检索器来将上面定义的方法组合到一起,示例代码如下:

from typing import List
from llama_index.core import QueryBundle
from llama_index.core.retrievers import BaseRetriever
from llama_index.core.schema import NodeWithScore
import asyncio

class FusionRetriever(BaseRetriever):
    """Ensemble retriever with fusion."""

    def __init__(
        self,
        retrievers: List[BaseRetriever],
        similarity_top_k: int = 2,
    ) -> None:
        """Init params."""
        self._retrievers = retrievers
        self._similarity_top_k = similarity_top_k
        super().__init__()

    def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:
        """Retrieve."""
        results = asyncio.run(
            run_queries(query_bundle.query_str, self._retrievers)
        )
        final_results = fuse_results(results, similarity_top_k=self._similarity_top_k)
        return final_results
  • 这个融合检索器的类继承自BaseRetriever类,重写了_retrieve方法

  • 构造方法中的参数retrievers是多个检索器的集合,similarity_top_k是最相似的结果数量

  • _retrieve方法中,调用了run_queries方法来获取检索结果results

  • 然后调用了fuse_results方法来融合检索结果并返回

我们来看融合检索器运行后的检索结果,代码示例如下:

fusion_retriever = FusionRetriever(
    [text_retriever, vector_retriever], similarity_top_k=2
)
question = "Which two members of the Avengers created Ultron?"
nodes = fusion_retriever.retrieve(question)
for node in nodes:
    print("-" * 50)
    print(f"node content: {node.text[:100]}...")
    print(f"node score: {node.score}\n")

# 显示结果
-----------------------------------------------node content: In the Eastern European country of Sokovia, the Avengers—Tony Stark, Thor, Bruce Banner, Steve Roger...
node score: 0.03306010928961749

-----------------------------------------------node content: Thor departs to consult with Dr. Erik Selvig on the apocalyptic future he saw in his hallucination, ...
node score: 0.016666666666666666
  • 首先定义了一个 FusionRetriever 对象,传入了全文检索器和向量检索器,同时设置了最相似的结果数量为 2

  • 然后传入了一个问题,获取检索结果

从结果中可以看到,检索结果节点返回的分数是经过 RRF 融合后的分数,分数值比较低,与原始的 Rerank 分数值不太匹配,这时我们可以使用 Rerank 模型来对检索结果进行重排序。

from llama_index.core.query_engine import RetrieverQueryEngine

rerank = CustomRerank(
    model="BAAI/bge-reranker-base", url="http://localhost:7007", top_n=2
)
Settings.llm = llm
query_engine = RetrieverQueryEngine(fusion_retriever, node_postprocessors=[rerank])
response = query_engine.query(question)
print(f"response: {response}")
for node in response.source_nodes:
    print("-" * 50)
    print(f"node content: {node.text[:100]}...")
    print(f"node score: {node.score}\n")

# 显示结果
response: Tony Stark and Bruce Banner.
-----------------------------------------------node content: In the Eastern European country of Sokovia, the Avengers—Tony Stark, Thor, Bruce Banner, Steve Roger...
node score: 0.8329173

-----------------------------------------------node content: Thor departs to consult with Dr. Erik Selvig on the apocalyptic future he saw in his hallucination, ...
node score: 0.24689633
  • CustomRerank类是一个自定义的 Rerank 类

  • 在系统设置中设置了 LLM 模型来生成答案

  • 通过混合检索器构建查询引擎,并在node_postprocessors参数中传入了 Rerank 模型,表示在检索结果后使用 Rerank 模型对检索结果进行重排序

  • 最后传入问题,获取检索结果

从结果中可以看到,检索结果节点返回的分数是经过 Rerank 模型重排序后的分数,分数值比较高,这样我们的混合检索系统就构建完成了。

总结

混合检索是一种在 RAG 应用中常用的检索策略,通过融合多种检索方式,可以提高检索的准确性和效率。今天我们通过 LlamaIndex 的代码实践,了解了构建混合检索系统的流程,同时也学习了如何使用 Llama3 和 ElasticSearch 来实现混合检索的效果,以及混合检索中一些常见的检索策略和排序算法。

关注我,一起学习各种人工智能和 AIGC 新技术,欢迎交流,如果你有什么想问想说的,欢迎在评论区留言。

用通俗易懂的方式讲解系列

  • 重磅来袭!《大模型面试宝典》(2024版) 发布!

  • 重磅来袭!《大模型实战宝典》(2024版) 发布!

  • 用通俗易懂的方式讲解:不用再找了,这是大模型最全的面试题库

  • 用通俗易懂的方式讲解:这是我见过的最适合大模型小白的 PyTorch 中文课程

  • 用通俗易懂的方式讲解:一文讲透最热的大模型开发框架 LangChain

  • 用通俗易懂的方式讲解:基于 LangChain + ChatGLM搭建知识本地库

  • 用通俗易懂的方式讲解:基于大模型的知识问答系统全面总结

  • 用通俗易懂的方式讲解:ChatGLM3 基础模型多轮对话微调

  • 用通俗易懂的方式讲解:最火的大模型训练框架 DeepSpeed 详解来了

  • 用通俗易懂的方式讲解:这应该是最全的大模型训练与微调关键技术梳理

  • 用通俗易懂的方式讲解:Stable Diffusion 微调及推理优化实践指南

  • 用通俗易懂的方式讲解:大模型训练过程概述

  • 用通俗易懂的方式讲解:专补大模型短板的RAG

  • 用通俗易懂的方式讲解:大模型LLM Agent在 Text2SQL 应用上的实践

  • 用通俗易懂的方式讲解:大模型 LLM RAG在 Text2SQL 上的应用实践

  • 用通俗易懂的方式讲解:大模型微调方法总结

  • 用通俗易懂的方式讲解:涨知识了,这篇大模型 LangChain 框架与使用示例太棒了

  • 用通俗易懂的方式讲解:掌握大模型这些优化技术,优雅地进行大模型的训练和推理!

  • 用通俗易懂的方式讲解:九大最热门的开源大模型 Agent 框架来了

参考:

[1]LlamaIndex: https://www.llamaindex.ai/
[2] Llama3: https://llama.meta.com/llama3/
[3]ElasticSearch: https://www.elastic.co/cn/elasticsearch/
[4]Ollama: _https://ollama.com/
[5]vllm: https://github.com/vllm-project/vllm
[7]自定义 LLM: https://docs.llamaindex.ai/en/stable/module_guides/models/llms/usage_custom/
[9] TEI: https://github.com/huggingface/text-embeddings-inference
[10]Xinference: https://inference.readthedocs.io/en/latest/
[12]BAAI/bge-base-en-v1.5: https://huggingface.co/BAAI/bge-base-en-v1.5
[13]BAAI/bge-reranker-base: https://huggingface.co/BAAI/bge-reranker-base
[14]复仇者联盟: https://en.wikipedia.org/wiki/Avenger

[16] ELSER 模型: https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html
[17] 这里: https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf

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

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

相关文章

Generative AI原理本质、技术内核及工程实践之基于Vertex AI的大模型 (四)Vertex AI 如何将 LLM 提升到新水平

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (一) 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (二) 基于 LlaMA 3 LangGraph 在windows本地部署大模型 (三) 基于 LlaMA…

[论文笔记]Query Rewriting for Retrieval-Augmented Large Language Models

引言 今天带来论文Query Rewriting for Retrieval-Augmented Large Language Models的笔记。 本篇工作从查询重写的角度介绍了一种新的框架,即重写-检索-阅读,而不是以前的检索-阅读方式,用于检索增强的LLM。关注的是搜索查询本身的适应性&…

服饰进口清关流程及注意事项 | 国际贸易数字化平台 | 箱讯科技

随着全球化进程的不断推进,我国消费者对国外品牌服饰的需求日益增长,衣服进口业务也随之蓬勃发展。作为一名从事进口衣服行业的专业人士,掌握清关流程及注意事项至关重要。本文将为您详细解析衣服进口清关流程,并提供一些实用建议…

【CHIP】LTC2991 读取温度电压电流 调试实例

文章目录 0. ENV1. LTC2991 数据说明1. 数据计算公式2. 寄存器概述1. 管脚使能寄存器2. 芯片使能寄存器 2. 软件实现1. 概述2. 源码(部分)3. 参考log 0. ENV 软件系统:略 LTC2991:VCC3.3 温度:温控接v1-v2 / v2-v3 / … (双端采样)电压&#…

怎么找回Excel表格密码,两个方法,轻松解密

有时候,打开很久以前的工作表,想要进行编辑,结果工作表设置了工作表保护,需要输入密码来撤消工作表保护,然而,自己又忘记了保护工作表的密码,怎么办? 1、使用备份文件 如果您有文件…

龙迅LT9211D MIPIDSI/CSI桥接到2 PORT LVDS,支持 3840x2160 30Hz分辨率

龙迅LT9211D描述: LT9211D是一款高性能的MIPI DSI/CSI-2到双端口LVDS转换器。LT9211D反序列化输入的MIPI视频数据,解码数据包,并将格式化的视频数据流转换为AP和移动显示面板或摄像机之间的LVDS发射机输出。LT9211D支持最大12.5 dB输入均衡和…

for语句初识

情景导入 某校某年级某班某位男生很爱哭,“wa”、“wa”、“wa”声音经常不绝于耳,现在请你通过编程来模拟他的哭声,他每发出一次哭声,则你输出一行——一个“wa”; 他哭了2次,我们可以这样写: …

力扣每日一题 6/11

博客主页:誓则盟约系列专栏:IT竞赛 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ 419.甲板上的战舰[中等] 题目: 给你一个大小为 m x n 的矩阵 b…

经典且完全免费的图像编辑处理软件比PS高级

一、简介 1、一款非常经典且完全免费的图像编辑处理软件,相比PS动辄1G的安装包,GIMP只有100 多兆,却包含了几乎所有常见的图片处理和平面设计相关功能,堪称PS替代品。 2、GIMP 几乎能实现与 PS 等同的功能,我们常使用…

【鸿蒙开发教程】HarmonyOS 模块关系梳理

HarmonyOS 梳理模块关系 刚开始开发的时候总是理不清鸿蒙中的模块类型和关系,今天就来梳理下鸿蒙中的模块类型 Module类型 Module按照使用场景可以分为两种类型: ●Ability类型的Module: 用于实现应用的功能和特性。每一个Ability类型的M…

抖音快手AI无人直播系统:教你快速搭建视频循环直播场景只需五部

AI无人直播是一种创新的直播方式,利用先进的技术手段实现自动直播,无需人工干预。这种直播方式具有全天候自动直播的能力,无需运营和监管即可吸引流量并转化为订单。商家门店对这种低成本高效果的方式非常欢迎。通过轻松进行直播销售&#xf…

【C语言】一篇带你高强度解析精通 字符串函数和内存函数 (万字总结大全,含思维导图)(建议收藏!!!)

【 库函数】——字符串函数和内存函数 目录 思维导图: 一:字符串函数 1.1:字符串常规函数 1.1.1:长度不受限制的字符串函数 1.1.1.1:strlen函数 1.1.1.2:strcpy函数 1.1.1.3:strcat函数 …

工业镜头孔径、分辨率与景深参数简析

工业镜头孔径、分辨率与景深参数简析 一、前言二、镜头孔径与光圈系数2.1孔径与相对孔径2.2数值孔径(NA)2.3光圈系数(F/#)2.4工作光圈系数((F/#)w) 三、镜头分辨率3.1MTF与CTF3.1.1M…

干货分享!2024年Instagram营销必备插件

Instagram是营销人员常用的社交媒体平台,通过提升品牌知名度来推动业务增长。今天给大家分享一些超实用的Instagram营销插件,无论是下载图片视频,还是预先发布帖子,这些工具都可以是你的得力助手,让你的INS运营效率蹭蹭…

阿里云百炼开发AI大模型详解

AI项目功能设想描述文档 随着AI发展越来越迅速,各行各业都需考虑如何将AI结合到自己的产品中,目前国内大部分的AI问答网站,都是基于Open AI实现的,但是如何需要运用到企业产品中那我们考虑的因素就会比较多 将ChatGpt移植到企业中…

Activiti7 Maven笔记

通过maven完成BPMN的创建,定义流程,部署流程,完成流程等操作 代码整合创建maven项目添加log4j日志配置添加activiti配置文件创建数据库 activitijava类编写程序生成表如果代码运行,没有生成表,可能是没有读取到activiti的配置文件 Activiti数据表介绍类关系图工作流引擎创建默认…

AI产品经理的转行之路,如何迈向年薪80w的职业高峰?

前言 在当今科技日新月异的时代,AI产品经理作为一个炙手可热的职业,吸引了众多向往高薪与前沿领域结合的求职者的目光。年薪80万的诱惑力无疑是巨大的,但不少自学中的朋友发现,即便涉猎广泛的产品知识,想要顺利转型成…

力扣 42. 接雨水

题目来源&#xff1a;https://leetcode.cn/problems/trapping-rain-water/description/ C题解1&#xff1a;双指针 按列算&#xff0c;一列一列的求雨水面积。使用双指针是记录当前列左右侧的最大元素。 class Solution { public:int trap(vector<int>& height) {in…

中电联系列三:rocket手把手教你理解中电联协议!

分享《慧哥的充电桩开源SAAS系统&#xff0c;支持汽车充电桩、二轮自行车充电桩。》 前 言 T/CEC102《电动汽车充换电服务信息交换》共分为四个部分&#xff1a; ——第1部分&#xff1a;总则&#xff1b; ——第2部分&#xff1a;公共信息交换规范&#xff1b; ——第3部分&a…

【ARM Cache 及 MMU 系列文章 1.3 -- 如何判断 L2 Cache 是否实现】

请阅读【ARM Cache 及 MMU/MPU 系列文章专栏导读】 及【嵌入式开发学习必备专栏】 文章目录 CPU Configuration Register代码实现CPU Configuration Register 在 armv9 架构中,我们可以通过arm 提供的自定义寄存器IMP_CPUCFR_EL1 来判断当前系统中是否实现了 L2 Cache, 如下所…