深度学习:基于MindNLP的RAG应用开发

什么是RAG?

RAG(Retrieval-Augmented Generation,检索增强生成) 是一种结合检索(Retrieval)和生成(Generation)的技术,旨在提升大语言模型(LLM)生成内容的准确性、相关性和时效性。

  • 基本思想:通过外部知识库动态检索与用户查询相关的信息,并将检索结果作为上下文输入生成模型,辅助生成更可靠的回答。

  • 与传统LLM的区别:传统LLM仅依赖预训练参数中的静态知识,而RAG能实时利用外部数据,解决模型幻觉(编造信息)和知识过时的问题。

RAG分为两阶段流程:

  1. 检索阶段(Retrieval)

    • 将用户查询转换为向量或关键词,从外部知识库(如文档、数据库、网页)中检索相关内容。

    • 常用技术:向量数据库(如FAISS)、BM25算法、语义相似度匹配。

  2. 生成阶段(Generation)

    • 将检索到的信息与用户输入拼接,输入生成模型(如GPT、LLaMA)生成最终回答。

    • 模型结合检索内容与自身知识,生成更精准、可解释的响应。

为什么需要RAG?

一、突破静态知识的限制

  1. 知识过时
    模型训练完成后无法自动更新知识(如GPT-3的数据截止到2021年)。
    示例:若用户询问“2023年诺贝尔奖得主是谁”,传统模型无法回答,而RAG可通过检索最新新闻或数据库提供正确答案。

  2. 覆盖范围有限
    模型对长尾领域(如企业内部文档、专业论文)或小众问题(如特定设备故障代码)可能缺乏知识。

二、减少模型“幻觉”风险

大模型生成内容时可能编造看似合理但错误的信息(称为“幻觉”),这在关键场景(如医疗、法律)中风险极高。

三、无需重新训练,低成本扩展能力

传统方法需通过微调(Fine-tuning) 让模型适配新任务,但存在痛点:

  1. 数据需求高:需大量标注数据,成本高昂。

  2. 难以频繁更新:每次更新知识都需重新训练模型,效率低下

RAG的优势:

  • 动态扩展知识:仅需更新外部知识库(如上传最新文档),无需修改模型参数。
  • 灵活适配场景:同一模型可连接不同知识库,服务医疗、金融等多个领域。

RAG的工作流程

一、构建知识库

1. 文本分块:将长篇文档切分为多个文本块,一边更加高效地处理和检索信息。

2. 向量化:使用各种Embedding模型来将每个文本块转换为高纬向量表示,使得文本之间可以在高维空间中进行相似度比较和检索。

二、用户检索

1. 向量化:将用户提出的问题,经过同样的向量化处理,得到问题的向量表示。

2. 检索:通过向量数据库进行相似性检验(如余弦相似度),从知识库中找到与用户提问相关的文档切片。

3. 响应:将检索到的文本块作为额外的上下文,加上用户的问题,生成更加规范正确的答案。

基于Mindspore的RAG实现

首先,需要在ModelArts平台创建:mindspore == 2.3.0, cann == 8.0.0的notebook。

随后,克隆github项目:

克隆MindNLP并更新

git clone https://github.com/mindspore-lab/mindnlp.git
cd mindnlp
git checkout ef64a3b83097c9578bb0d5326f905beeb5b50e1d
bash scripts/build_and_reinstall.sh

RAG项目

git clone https://github.com/ResDream/MindTinyRAG.git

安装所需依赖

cd MindTinyRAG
pip install -r requirements.txt

一、 读取文件与处理

需求分析:

  • 文件格式不同,需要判断文件类型再进行内容提取。
  • 基于最大token长度和覆盖内容的逻辑分割长文本,确保段落间的语义连续性。
分块策略

1. 固定长度分块(Fixed-Size Chunks)

将文档按固定长度(如每段100字或500字符)均匀切割。

优点:实现简单,计算高效。适合结构化文本(如代码、表格)。

缺点:可能破坏句子或段落的语义完整性。对长距离依赖的上下文不敏感。

示例:

原文:人工智能(AI)是... [100字] → 分块1:人工智能(AI)是...;分块2:在医疗领域...;

2. 基于内容的分块(Content-Based Chunking)

根据文档结构(段落、标题、分隔符)进行分块。

常见方法

  • 段落分块:以自然段落为单位。
  • 标题分块:按标题层级切分(如H1、H2)。
  • 分隔符分块:根据标点(如句号)、空行或Markdown符号(##)分割。

优点:保留语义单元,减少上下文碎片化。适合非结构化文本(如文章、报告)。

缺点:依赖文档格式的规范性(如无明确分隔符时失效)。

示例:

# 第一章 引言
人工智能是... → 分块1(标题+段落)
## 1.1 发展历史
20世纪50年代... → 分块2

3. 滑动窗口分块(Sliding Window)

在固定长度分块基础上,通过重叠窗口(如50%重叠)连接上下文。

优点:缓解固定分块导致的上下文断裂问题。提升相邻块之间的语义连贯性。

缺点:增加存储和计算成本(块数量翻倍)。

示例:

窗口大小=200字符,重叠=50%  
分块1:字符1-200  
分块2:字符100-300  
分块3:字符200-400

4. 语义分块(Semantic Chunking)

利用NLP模型(如句子嵌入、文本分割模型)识别语义边界。

常用技术

  • 句子分割:按完整句子分块。
  • 主题分割:检测文本主题变化(如使用TextTiling算法)。
  • 嵌入聚类:通过向量相似度合并相关段落。

优点:保持语义完整性,适合长文本(如论文、书籍)。提升检索相关性。

缺点:计算复杂度高,依赖模型性能。

示例

原文:深度学习在CV中的应用...(技术细节)。另一方面,NLP领域... → 分块1(CV)、分块2(NLP)

5. 动态分块(Dynamic Chunking)

据查询需求动态调整分块粒度。

实现方式

  • 多粒度索引:同时存储不同粒度的块(如段落、章节、全文)。
  • 检索时合并:检索到多个相关小粒度块后合并为上下文。

优点:平衡召回率与噪声控制。适应多样化的查询需求。

缺点:系统设计复杂,需多层索引支持。

分块策略选择建议
  1. 平衡长度与语义

    • 一般场景:优先语义分块(如段落)或滑动窗口(重叠20-30%)。

    • 长文本(如书籍):语义分块+动态合并。

  2. 考虑下游模型限制

    • 块长度需适配生成模型的输入容量(如GPT-4最大支持128k tokens)。

  3. 领域适配

    • 技术文档:按函数/API分块;法律文本:按条款分块。

  4. 实验调优

    • 通过检索准确率、生成质量等指标测试最佳分块大小。

项目使用滑动窗口分块策略:

  • 分块大小(chunk size):定义每个分块的最大长度,通常以token为单位
  • 滑动步长(stride size):定义分块的起始位置之间的距离,从而实现分块之间的重叠。
  • 重叠内容(overlap):由 | chunk size - stride size | 决定,用于保留上下文

1. 确定每个文本块的最大长度(分块大小)

2. 确定滑动的步长,计算分块之间的重叠区域

3. 从文档头开始,提取长度为窗口大小的内容作为分块

4. 从前一个分块的起始位置向前滑动步长,提取下一段内容

5. 重复步骤4直至处理完整个文档。

定义ReadFile类:
# 定义读取文件的类
class ReadFiles:
    '''
        class for reading files with suffixes
    '''
    
    def __init__(self, path: str) -> None:
        '''
            path: 知识库的文件路径
            file_list:文件对象的列表
        '''
        self._path = path
        self.file_list = self.get_files()
    
    @classmethod
    def read_pdf(cls, file_path: str) -> str:
        '''
            使用PyPDF读取pdf文件,并返回文件中文本的方法
            
            param:
                file_path: 文件路径
            
            return:
                text: PDF中的所有文本
        '''
        with open(file_path, 'rb') as file:
            reader = PyPDF2.PdfReader(file)
            text = ''
            for page_num in range(len(reader.pages)):
                text += reder.pages[page_num].extract_text()
            return text
    
    @classmethod
    def read_markdown(cls, file_path) -> str:
        '''
            使用bs4读取md文件,并返回文件中文本的方法
            
            param:
                file_path: 文件路径
            
            return:
                text: PDF中的所有文本
        '''
        with open(file_path, 'r', encoding='utf-8') as file:
            md_text = file.read()
            html_text = markdown.markdown(md_text)
            soup = BeautifulSoup(html_text, 'html.parser')
            plain_text = soup.get_text()
            # 使用正则表达式移除网址链接
            text = re.sub(r'http\S+', '', plain_text) 
            return text
    
    @classmethod
    def read_txt(cls, file_path: str) -> str:
        '''
            返回文件中文本的方法
            
            param:
                file_path: 文件路径
            
            return:
                text: 文件文本
        '''
        with open(file_path, 'r', encoding='utf-8') as file:
            text = file.read()
            return text
    
    def get_files(self) -> list[str]:
        '''
            获取指定路径下的所有文件
            
            return:
             file_list:可以进行解析的所有文件的文件路径
        '''
        file_list =[]
        for filepath, dirnames, filenames in os.walk(self._path):
            # os.walk 函数将递归遍历指定文件夹
            for filename in filenames:
                # 通过后缀名判断是否能进行解析
                if filename.endswith('.md'):
                    file_list.append(os.path.join(filepath, filename))
                elif filename.endswith('.txt'):
                    file_list.append(os.path.join(filepath, filename))
                elif filename.endswith(".pdf"):
                    file_list.append(os.path.join(filepath, filename))
        return file_list

    def get_content(self, max_token_len: int = 600, cover_content: int = 150) -> list[str]:
        '''
            获取知识库中所有文件的文件内容
            
            param:
                max_token_len: 分块大小
                cover_conten: 重叠内容大小
            
            return:
                docs: 文件内容
        '''
        docs = []
        
        for file in self.file_list:
            # 获取文件内容
            content = self.read_file_content(file)
            # 将文件内容分块
            chunk_content = self.get_chunk(
            content, max_token_len=max_token_len, cover_content=cover_content)
            
            docs.extend(chunk_content)
        
        return docs
    
    @classmethod
    def get_chunk(cls, text: str, max_token_len: int = 600, cover_content: int = 150):
        chunk_text = []
        # 当前块长度
        curr_len = 0
        # 当前块内容
        curr_chunk = ''
        # 滑动窗口的滑动步长
        token_len = max_token_len - cover_content
        # 假设以换行符对文本进行分割
        lines = text.splitlines()
        for line in lines:
            # 去除文本中的空格
            line = line.replace(' ', '')
            # enc.encode: 将文本转换为 token ID 列表
            line_len = len(enc.encode(line))
            # 如果文本较长,对文本进行分块(处理超长行)
            if line_len > max_token_len:
                # 计算一共要切成多少个文本块
                num_chunks = (line_len + token_len - 1) // token_len
                for i in range(num_chunks):
                    start = i * token_len
                    end = start + token_len
                    # 确保每个块的结尾不会截断一个完整的单词,从而保持语义的连贯性。
                    # 通过循环逐步右移切割点,直到块末尾落在空白字符处:
                    # 如果末尾不是空白,将 start 和 end 右移一位,直到满足条件或越界。
                    while not line[start: end].rstrip().isspace():
                        start += 1
                        end += 1

                        if start >= line_len:
                            break
                    # 前一个块的末尾 cover_content 字符与当前切割内容拼接:
                    curr_chunk = curr_chunk[-cover_content: ] + line[start: end]
                    chunk_text.append(curr_chunk)
                # 处理最后一个块
                # 循环内的 end 可能超出文本实际长度,需要单独处理最后一个块以修正范围。
                start = (num_chunks - 1) * token_len
                curr_chunk = curr_chunk[-cover_content:] + line[start:end]
                chunk_text.append(curr_chunk)
            
            # 如果不是超长文本,直接处理
            # 如果当前文本还没有到达最长长度,先拼接,不添加
            if curr_len + line_len <= token_len:
                curr_chunk += line
                curr_chunk += '\n'
                curr_len += line_len
                curr_len += 1
            # 如果达到最长长度,直接添加
            else:
                chunk_text.append(curr_chunk)
                curr_chunk = curr_chunk[-cover_content: ] + line
                curr_len = line_len + cover_content
                
        if curr_chunk:
            chunk_text.append(curr_chunk)
        return chunk_text
    
    @classmethod
    def read_file_content(cls, file_path: str):
        # 根据文件扩展名选择读取方法
        if file_path.endswith('.pdf'):
            return cls.read_pdf(file_path)
        elif file_path.endswith('.md'):
            return cls.read_markdown(file_path)
        elif file_path.endswith('.txt'):
            return cls.read_text(file_path)
        else:
            raise ValueError("Unsupported file type")

重点:get_chunk算法

该分块算法的核心实现思路可总结为以下流程:

首先基于换行符将原始文本分割为行级单位,假设“行”是天然语义边界;随后逐行处理时,针对两种场景采取不同策略。

普通行通过动态累加至当前块(curr_chunk),若累计 token 长度(curr_len)超出步长限制(token_len = max_token_len - cover_content),则将当前块存入结果并基于重叠机制(保留末尾 cover_content 字符)初始化新块

超长行则按步长预切割后,通过逐字符右移切割点使块末尾落于空格处,避免单词截断,同时每个新块强制拼接前一块的重叠内容以维持上下文连贯性。

算法通过隐式覆盖(非显式重置)管理状态变量,最终保证所有块长度不过 max_token_len 且相邻块间存在可控重叠。其优势在于平衡了切割效率与语义完整性,但需注意空格删除对语义的破坏风险及冗余块处理问题,可通过反向空格查找、精确换行符 token 计算和状态隔离进一步优化。

二、定义Embedding类

# Embedding设计
class BaseEmbeddings:
    '''
        embedding的基类
    '''
    def __init__(self, path: str, is_api: bool) -> None:
        self.path = path
        self.is_api = is_api
    
    def get_embedding(self, text: str, model: str) -> list[float]:
        '''
            待子类实现此方法
        '''
        raise NotImplementedErrort
    
    @classmethod
    def consine_similarity(cls, vector1: list[float], vector2: list[float]) -> float:
        '''
            计算两个向量的余弦相似度
        '''
        dot = np.dot(vector1, vector2)
        magnitude = np.linalg.norm(vector1) * np.linalg.norm(vector2)
        if not magnitude:
            return 0
        return dot / magnitude
class MindNLPEmbedding(BaseEmbeddings):
    '''
        MindNLP使用的Embedding类,继承了BaseEmbedding
    '''
    def __init__(self, path='BAAI/bge-base-zh-v1.5', is_api=False):
        super().__init__(path, is_api)
        self._model = self.load_model(path)
    
    def get_embedding(self, text: str):
        '''
            使用定义的Embedding模型,获取输入句子的Embedding
        '''
        sentence_embedding = self._model.encode([text], normalize_embeddings=True)
        return sentence_embedding
    
    def load_model(self, path: str):
        '''
            通过MindNLP提供的类,加载Embedding模型并返回
        '''
        from mindnlp.sentence import SentenceTransformer
        model = SentenceTransformer(path)
        return model
    
    @classmethod
    def consine_similarity(cls, sentence_embedding_1, sentence_embedding_2):
        '''
            重写父类的相似度方法
            子类在调用 self._model.encode 时设置了 normalize_embeddings=True,
            这会强制将输出向量归一化为单位长度(模长为 1)。
            所以相似度只需要计算点积结果即可表示。
        '''
        similarity = sentence_embedding_1 @ sentence_embedding_2.T
        return similarity

三、知识库设计

向量数据库(Vector Database)是一种专门用于存储、管理和高效检索高维向量数据的数据库系统。它的核心目标是解决传统数据库难以处理的大规模高维数据相似性搜索问题,广泛应用于人工智能、机器学习和大数据领域。

常用的向量数据库有Meta AI团队提供的FAISS或华为的GaussDB for Vector

本案例将自己实现一个简单的向量数据库。

需求分析:

  1. Embedding计算
  2. 数据持久化存储
  3. 从文件中加载数据
  4. 相似文档查询
class VectorStore:
    def __init__(self, document: list[str] = ['']):
        '''
            初始化向量库
            
            param:
                document: 所有已经分块好的文本块
        '''
        self.document = document
    
    def get_vector(self, EmbeddingModel: BaseEmbeddings):
        '''
            获取所有文本块的向量
        '''
        self.vetors = []
        for doc in tqdm(self.document, desc='Calculating Embeddings'):
            # 通过Embedding类中定义好的方法,获取每个文本块的向量
            self.vetors.append(EmbeddingModel.get_embedding(doc))
        return self.vetors
    
    def persits(self, path: str = 'storage'):
        '''
            将向量数据保存到本地
        '''
        if not os.path.exists(path):
            os.makedirs(path)
        # 持久化存储文档
        with open(f"{path}/document.json", 'w', encoding='utf-8') as f:
            json.dump(self.document, f ,ensure_ascii=False)
        # 持久化存储向量
        if self.vetors:
            vectors_list = [vector.tolist() for vector in self.vetors]
            with open(f"{path}/vectors.json", 'w', encoding='utf-8') as f:
                json.dump(vectors_list, f)
        
    def load_vector(self, EmbeddingModel: BaseEmbeddings, path: str = 'storage'):
        with open(f"{path}/document.json", 'r', encoding='utf-8') as f:
            self.document = json.load(f)
        with open(f"{path}/vectors.json", 'r', encoding='utf-8') as f:
            vectors_list = json.load(f)
        # 判断EmbeddingModel的类型
        if isinstance(EmbeddingModel, MindNLPEmbedding):
            self.vetors = [np.array(vector) for vector in vectors_list]
        else:
            self.vetors = vectors_list
    
    def get_similarity(self, query: str, EmbeddingModel: BaseEmbeddings, k: int = 1):
        # 获取query的向量
        query_vector = EmbeddingModel.get_embedding(query)

        # 计算查询向量和所有文本块的相似度
        similaraties = [self.get_similarity(query_vector, vector) for vector in self.vetors]
        # 将相似度、向量和文档存储在一个列表中
        results = []
        for similarity, vector, document in zip(similaraties, self.vetors, self.document):
            results.append({
                'similarity': similarity,
                'vector': vector,
                'document': document
            })
        # 将结果按照相似度降序排序
        results.sort(key=lambda x: x['similarity'], reverse=True)
        # 选取相似度最高的top_k个结果
        top_k_documents  = [result['document'] for result in results[:k]]
        return top_k_documents

四、定义模型

# 定义语言模型
class BaseModel:
    def __init__(self, path: str = ''):
        self.path = path
    def chat(self, prompt: str, history: list[dict], content: str) -> str:
        pass
    def load_model(self):
        pass

# 定义prompt模板
PROMPT_TEMPLATE = dict(
    RAG_PROMPT_TEMPALTE="""使用以上下文来回答用户的问题。如果你不知道答案,请输出我不知道。总是使用中文回答。
        问题: {question}
        可参考的上下文:
        ···
        {context}
        ···
        如果给定的上下文无法让你做出回答,请回答数据库中没有这个内容,你不知道。
        有用的回答:""",
    MindNLP_PROMPT_TEMPALTE="""先对上下文进行内容总结,再使用上下文来回答用户的问题。如果你不知道答案,请输出我不知道。总是使用中文回答。
        问题: {question}
        可参考的上下文:
        ···
        {context}
        ···
        如果给定的上下文无法让你做出回答,请回答数据库中没有这个内容,你不知道。
        有用的回答:"""
)

class MindNLPChat(BaseModel):
    def __init__(self, path: str = ''):
        super().__init__(path)
        self.load_model()
    
    def chat(self, prompt: str, history: list = [], content: str = '') -> str:
        """
            生成对话回复。
            
            Args:
                prompt (str): 用户输入的提示文本。
                history (list[dict]): 对话历史,每个元素为包含'user'和'assistant'键的字典。
                content (str): 上下文内容,用于增强生成的相关性。
                
            Returns:
                str: 模型生成的回复文本。
        """
        prompt = PROMPT_TEMPLATE['MindNLP_PROMPT_TEMPALTE'].format(question=prompt, context=content)
        resp, history = self.model.chat(self.tokenizer, prompt, history=history, max_length=1024)
        return resp
    
    def load_model(self):
        import mindspore
        from mindnlp.transformers import AutoTokenizer, AutoModelForCausalLM
        self.tokenizer = AutoTokenizer.from_pretrained(self.path, mirror="huggingface")
        self.model = AutoModelForCausalLM.from_pretrained(self.path, ms_dtype=mindspore.float16, mirror="huggingface")

五、重排序ReRank

Rerank(重排序)技术是优化检索结果的关键环节。它的核心目标是从初步检索得到的候选文档中,筛选出最相关、高质量的文档作为生成模型的输入,从而提升最终答案的准确性和相关性。

在RAG的典型流程中,Rerank位于检索(Retrieval)生成(Generation)之间:

  1. 检索阶段:使用快速但粗粒度的检索模型(如BM25、双编码器Bi-Encoder)从海量文档中召回Top-K候选(例如K=100)。

  2. Rerank阶段:对Top-K候选进行精细排序,选出最相关的Top-N(例如N=5)文档。

  3. 生成阶段:基于Top-N文档生成最终答案。

class MindNLPReranker(BaseReranker):
    def __init__(self, path: str = 'BAAI/bge-reranker-base'):
        super().__init__(path)
        self._model = self.load_model(path)
    
    def load_model(self, path: str):
        from mindnlp.sentence import SentenceTransformer
        model = SentenceTransformer(path)
        return model
    
    def rerank(self, text, content, k):
        query_embedding = self._model.encode(text, normalize_embeddings=True)
        sentences_embedding = self._model.encode(content, normalize_embeddings=True)
        similarity = query_embedding @ sentences_embedding.T
        # 根据相似度降序排序
        reanked_index = np.argsort(similarity)[::-1]
        # 选择top k个结果
        top_k = [content[i] for i in reanked_index[:k]]
        return top_k

# 创建RerankerModel
reranker = MindNLPReranker('BAAI/bge-reranker-base')
# 从向量数据库中查询出最相似的3个文档
content = vector.query(question, EmbeddingModel=embedding, k=3)
print('first query', content)
# 从一阶段查询结果中用Reranker再次筛选出最相似的2个文档
rerank_content = reranker.rerank(question, content, k=2)
print('reranked', rerank_content)

# 最后选择最相似的文档, 交给LLM作为可参考上下文
best_content = rerank_content[0]
print(chat.chat(question, [], best_content))

运行代码:

embedding = MindNLPEmbedding("BAAI/bge-base-zh-v1.5")
vector = VectorStore(text)
vector.get_vector(EmbeddingModel=embedding)
vector.persists(path='storage')  # 将向量和文档内容保存到storage目录下,下次再用就可以直接加载本地的数据库
vector.load_vector(EmbeddingModel=embedding, path='./storage')  # 加载本地的数据库

# 创建RerankerModel
reranker = MindNLPReranker('BAAI/bge-reranker-base')
# 从向量数据库中查询出最相似的3个文档
content = vector.query(question, EmbeddingModel=embedding, k=3)
print('first query', content)
# 从一阶段查询结果中用Reranker再次筛选出最相似的2个文档
rerank_content = reranker.rerank(question, content, k=2)
print('reranked', rerank_content)

# 最后选择最相似的文档, 交给LLM作为可参考上下文
best_content = rerank_content[0]
print(chat.chat(question, [], best_content))

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

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

相关文章

零刻SER7接口及配置跑分

今天入手了一台迷你机-零刻SER7 &#xff0c;不得不说这机身是真的小啊&#xff0c;相比于传统台式机&#xff0c;它几乎不占空间&#xff0c;可以轻松放置在桌面、电视柜甚至背包中&#xff0c;非常适合需要频繁移动或空间有限的用户。尽管体积小巧&#xff0c;但零刻SER7在性…

ResNeSt: Split-Attention Networks 参考论文

参考文献 [1] Tensorflow Efficientnet. https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet. Accessed: 2020-03-04. 中文翻译&#xff1a;[1] TensorFlow EfficientNet. https://github.com/tensorflow/tpu/tree/master/models/official/efficien…

能够对设备的历史数据进行学习与分析,通过与设备当前状态的比对,识别潜在故障并做出预判的名厨亮灶开源了。

明厨亮灶视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。AI技术可以24小时…

大数据学习之Kafka消息队列、Spark分布式计算框架一

Kafka消息队列 章节一.kafka入门 4.kafka入门_消息队列两种模式 5.kafka入门_架构相关名词 Kafka 入门 _ 架构相关名词 事件 记录了世界或您的业务中 “ 发生了某事 ” 的事实。在文档中 也称为记录或消息。当您向 Kafka 读取或写入数据时&#xff0c;您以事件的 形式执行…

爱书爱考平台说明

最近我开发了一个综合性的考试平台&#xff0c;内容包括但不限于职业资格证考试、成人教育、国家公务员考试等内容。目前1.0版本已经开发完成&#xff0c;其他的功能陆续完善中。 微信小程序搜索"爱书爱考" 微信小程序图标如下图: 目前维护了java相关的面试题的考题…

Docker/K8S

文章目录 项目地址一、Docker1.1 创建一个Node服务image1.2 volume1.3 网络1.4 docker compose 二、K8S2.1 集群组成2.2 Pod1. 如何使用Pod(1) 运行一个pod(2) 运行多个pod 2.3 pod的生命周期2.4 pod中的容器1. 容器的生命周期2. 生命周期的回调3. 容器重启策略4. 自定义容器启…

2023年吉林省职业院校技能大赛网络系统管理样题-网络配置(华三代码)

目录 附录1:拓扑图 附录2:地址规划表 1.S1 2.S3 3.S4 4.S5 5.S7 6.S8 7.S9 8.R1 9.R2 10.R3 11.EG1 12.EG2 13.AC1 14.AC2 附录1:拓扑图 编号 型号

HTML-新浪新闻-实现标题-排版

标题排版 图片标签&#xff1a;<img> src&#xff1a;指定图片的url&#xff08;绝对路径/相对路径&#xff09; width&#xff1a;图片的宽度&#xff08;像素/相对于父元素的百分比&#xff09; heigth&#xff1a;图片的高度&#xff08;像素/相对于父元素的百分比&a…

基于物联网的智能环境监测系统(论文+源码)

1系统的功能及方案设计 本课题为基于物联网的智能环境监测系统的设计与实现&#xff0c;整个系统采用stm32f103单片机作为主控制器&#xff0c;通过DHT11传感器实现智能环境监测系统温度和湿度的检测&#xff0c;通过MQ传感器实现CO2浓度检测&#xff0c;通过光照传感器实现光照…

全面解析文件上传下载删除漏洞:风险与应对

在数字化转型的时代&#xff0c;文件上传、下载与删除功能已经成为各类应用程序的标准配置&#xff0c;从日常办公使用的协同平台&#xff0c;到云端存储服务&#xff0c;再到社交网络应用&#xff0c;这些功能在给用户带来便捷体验、显著提升工作效率的同时&#xff0c;也隐藏…

1.2第1章DC/DC变换器的动态建模-1.2Buck-Boost 变换器的交流模型--电力电子系统建模及控制 (徐德鸿)--读书笔记

1.2 Buck-Boost 变换器的交流模型 Buck- Boost变换器是一种典型的DC/DC变换器&#xff0c;具有升压和降压功能其输出电压的极性与输入电压相反&#xff0c;见图1-4a。当电感L的电流i(t)连续时一个开关周期可以分为两个阶段。在阶段1&#xff0c;开关在位置1时&#xff0c;即&am…

06-AD向导自动创建P封装(以STM32-LQFP48格式为例)

自动向导创建封装 自动向导创建封装STM32-LQFP48Pin封装1.选则4排-LCC或者QUAD格式2.计算焊盘相定位长度3.设置默认引脚位置(芯片逆时针)4.特殊情况下:加额外的标记 其他问题测量距离:Ctrl M测量 && Ctrl C清除如何区分一脚和其他脚?芯片引脚是逆时针看的? 自动向导…

若依基本使用及改造记录

若依框架想必大家都了解得不少&#xff0c;不可否认这是一款及其简便易用的框架。 在某种情况下&#xff08;比如私活&#xff09;使用起来可谓是快得一匹。 在这里小兵结合自身实际使用情况&#xff0c;记录一下我对若依框架的使用和改造情况。 一、源码下载 前往码云进行…

面试经典150题——图

文章目录 1、岛屿数量1.1 题目链接1.2 题目描述1.3 解题代码1.4 解题思路 2、被围绕的区域2.1 题目链接2.2 题目描述2.3 解题代码2.4 解题思路 3、克隆图3.1 题目链接3.2 题目描述3.3 解题代码3.4 解题思路 4、除法求值4.1 题目链接4.2 题目描述4.3 解题代码4.4 解题思路 5、课…

基于SpringBoot电脑组装系统平台系统功能实现六

一、前言介绍&#xff1a; 1.1 项目摘要 随着科技的进步&#xff0c;计算机硬件技术日新月异&#xff0c;包括处理器&#xff08;CPU&#xff09;、主板、内存、显卡等关键部件的性能不断提升&#xff0c;为电脑组装提供了更多的选择和可能性。不同的硬件组合可以构建出不同类…

万字长文总结前端开发知识---JavaScriptVue3Axios

JavaScript学习目录 一、JavaScript1. 引入方式1.1 内部脚本 (Inline Script)1.2 外部脚本 (External Script) 2. 基础语法2.1 声明变量2.2 声明常量2.3 输出信息 3. 数据类型3.1 基本数据类型3.2 模板字符串 4. 函数4.1 具名函数 (Named Function)4.2 匿名函数 (Anonymous Fun…

MySQL(单表访问)

今天是新年&#xff0c;祝大家新年快乐&#xff0c;但是生活还是得继续。 后面也会持续更新&#xff0c;学到新东西会在其中补充。 建议按顺序食用&#xff0c;欢迎批评或者交流&#xff01; 缺什么东西欢迎评论&#xff01;我都会及时修改的&#xff01; 大部分截图和文章采…

HarmonyOS简介:应用开发的机遇、挑战和趋势

问题 更多的智能设备并没有带来更好的全场景体验 连接步骤复杂数据难以互通生态无法共享能力难以协同 主要挑战 针对不同设备上的不同操作系统&#xff0c;重复开发&#xff0c;维护多套版本 多种语言栈&#xff0c;对人员技能要求高 多种开发框架&#xff0c;不同的编程…

【Elasticsearch】Elasticsearch的查询

Elasticsearch的查询 DSL查询基础语句叶子查询全文检索查询matchmulti_match 精确查询termrange 复合查询算分函数查询bool查询 排序分页基础分页深度分页 高亮高亮原理实现高亮 RestClient查询基础查询叶子查询复合查询排序和分页高亮 数据聚合DSL实现聚合Bucket聚合带条件聚合…

使用 KNN 搜索和 CLIP 嵌入构建多模态图像检索系统

作者&#xff1a;来自 Elastic James Gallagher 了解如何使用 Roboflow Inference 和 Elasticsearch 构建强大的语义图像搜索引擎。 在本指南中&#xff0c;我们将介绍如何使用 Elasticsearch 中的 KNN 聚类和使用计算机视觉推理服务器 Roboflow Inference 计算的 CLIP 嵌入构建…