【深度学习总结】使用PDF构建RAG:结合Langchain和通义千问

news2024/12/19 6:19:29

【深度学习总结】使用PDF构建RAG:结合Langchain和通义千问

使用平台:趋动云,注册送算力

前言

在大型语言模型(LLMs)应用领域,我们面临着大量挑战,从特定领域知识的匮乏到信息准确性的窘境,以及可能生成虚假内容。检索增强生成(RAG)通过引入外部知识库等补充信息源,成为解决这些难题的有效策略。事实证明,在需要持续更新或特定领域应用的知识密集型场景中,RAG 尤其有效。与其他方法相比,RAG 的一个显著优势在于无需为特定任务重新培训 LLM。最近,RAG 因其在会话助手等应用中的成功应用而备受瞩目。

RAG构成了一种将输入与相关支持文档的语料库相结合的技术。这些文档被合并到输入提示中,并联合输入到文本生成器中,从而产生最终的输出。这种RAG的这种机制在需要适应不断变化的信息环境的场景中找到了特殊的效用,因为llm所依赖的参数化知识本质上是静态的。通过RAG,语言模型可以直接访问最新的信息,而不需要再训练,促进了生成可靠的、基于检索的输出。本质上,RAG通过检索证据提高了LLM响应的准确性、可控性和相关性,从而证明了在快速发展的环境中解决问题,并有效地缓解了错误信息生成和性能退化的问题。

RAG的一个典型应用程序如下图所示。
在这里插入图片描述

在这里,一个用户向ChatGPT提出了一个关于最近一个被广泛讨论的新闻的问题。鉴于ChatGPT依赖于训练前的数据,它最初缺乏提供最新发展的能力。RAG通过从外部数据库中获取和整合知识来弥补这一信息差距。在这种情况下,它会收集与用户查询相关的相关新闻文章。这些文章,结合最初的问题,形成了一个全面的提示,使llm能够生成一个知情的答案。典型的RAG遵循了一个传统的过程,包括索引、检索和生成,这也被描述为一个“检索“。

索引:首先以不同的格式清理和提取原始数据,如PDF、HTML、Word和标记,然后将其转换为统一的纯文本格式。为了适应语言模型的上下文限制,文本被分割成更小的、可理解的块。然后使用嵌入模型将块编码到向量表示中,并存储在向量数据库中。这一步对于在后续的检索阶段实现有效的相似性搜索至关重要。

检索:在收到用户查询后,RAG系统使用在索引阶段使用的相同的编码模型来将查询转换为向量表示。然后计算查询向量和索引语料库中的块向量之间的相似性得分。系统对与查询相似性最大的前k块进行优先排序和检索。这些数据块随后在提示符中被用作扩展的上下文。

生成:所提出的查询和所选择的文档被合成成一个连贯的提示,一个大型语言模型负责制定一个响应。模型的回答方法可能根据特定任务的标准而有所不同,允许它利用其固有的参数知识或限制其对所提供文档中包含的信息的响应。在正在进行的对话的情况下,任何现有的对话历史都可以集成到提示符中,使模型能够有效地参与多回合的对话交互

教程

PDF文件

使用的是民用飞机维修的PDF文件,可以私信我获取,你也可以用自己的。

准备

  • 通义千问的API-Key
  • 运行环境:将如下内容存入requirements.txt,然后运行:pip install -r requirements.txt
python-dotenv==1.0.1 # For reading environment variables stored in .env file
langchain==0.2.2
langchain-community==0.2.3
dashscope
unstructured==0.14.4 # Document loading
# onnxruntime==1.17.1 # chromadb dependency: on Mac use `conda install onnxruntime -c conda-forge`
# For Windows users, install Microsoft Visual C++ Build Tools first
# install onnxruntime before installing `chromadb`
chromadb==0.5.0 # Vector storage
tiktoken==0.7.0  # For embeddings 

构建RAG

先导入包:

from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.schema import Document
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings
from dotenv import load_dotenv
import os
import shutil
import dashscope
from dashscope import Generation
from langchain.prompts import ChatPromptTemplate
from http import HTTPStatus

然后将PDF数据转换为向量存储起来:

# Load environment variables. Assumes that project contains .env file with API keys
load_dotenv()
# 设置镜像,便于下载后面的HuggingFaceEmbeddings
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
# huggingface下载地址
os.environ["HF_HOME"] = "/gemini/code/huggingface"
# huggingface下载地址
os.environ["TRANSFORMERS_CACHE"] = "/gemini/code/huggingface"

os.environ["SENTENCE_TRANSFORMERS_HOME"] = "/gemini/code/huggingface/bce-embedding-base_v1"

# 向量存放位置
CHROMA_PATH = "chroma"
# 存放数据
DATA_PATH = "data/books"
dashscope.api_key = "你的通义千问api key"
print(os.getenv('DASHSCOPE_API_KEY'))
def prepare_db():
    # 处理读个pdf
    pdf_paths = ["data/books/M1.pdf",
                 "data/books/M2-航空器维修R1.pdf",
                 "data/books/M3-飞机结构和系统R1.pdf",
                 "data/books/M4-直升机结构和系统.pdf",
                 "data/books/M5-航空涡轮发动机R1.pdf",
                 "data/books/M6-活塞发动机及其维修.pdf",
                 "data/books/M7-航空器维修基本技能.pdf",
                 "data/books/M8-航空器维修实践R1.pdf"]
    documents = []
    count = 0
    for pdf_path in pdf_paths:

        loader = PyPDFLoader(pdf_path)
        doc = loader.load()
        documents.extend(doc)
        count += 1
        print(f"处理第{count}本")

    print(len(documents))

    text_splitter = RecursiveCharacterTextSplitter(
        chunk_size=300,
        chunk_overlap=100,
        length_function=len,
        add_start_index=True,
    )
    chunks = text_splitter.split_documents(documents)
    print(f"Split {len(documents)} documents into {len(chunks)} chunks.")

    document = chunks[10]
    print(document.page_content)
    print(document.metadata)

    if os.path.exists(CHROMA_PATH):
        shutil.rmtree(CHROMA_PATH)
    # 将文本保存为向量存储
    model_name = "maidalun1020/bce-embedding-base_v1"
    model_kwargs = {'device': 'cuda'}
    encode_kwargs = {'normalize_embeddings': False}
    embeddings = HuggingFaceEmbeddings(
        model_name=model_name,
        model_kwargs=model_kwargs,
        encode_kwargs=encode_kwargs,
        cache_folder="/gemini/code/huggingface/",
    )
    # Create a new DB from the documents.
    db = Chroma.from_documents(
        chunks, embeddings, persist_directory=CHROMA_PATH
    )
    db.persist()
    print(f"Saved {len(chunks)} chunks to {CHROMA_PATH}.")

然后调用通义千问的API,用户输入问题,然后根据问题从向量库中查找相关的内容,跟问题结合起来,一起喂给通义千问:

def query():
    model_name = "maidalun1020/bce-embedding-base_v1"
    model_kwargs = {'device': 'cuda'}
    encode_kwargs = {'normalize_embeddings': False}
    embeddings = HuggingFaceEmbeddings(
        model_name=model_name,
        model_kwargs=model_kwargs,
        encode_kwargs=encode_kwargs,
        # 模型缓存路径
        cache_folder="/gemini/code/huggingface/",
    )
    # 改成你的保存的路径
    db = Chroma(persist_directory="./chroma/aae4fae7-3477-4094-8a7d-c5df8be2223a", embedding_function=embeddings)
	# 提示模板
    PROMPT_TEMPLATE = """
    仅根据下列文本回答问题:
    {context}
    """
    while True:
        query = input('请输入问题:')
        results = db.similarity_search_with_relevance_scores(query, k=5)
        if len(results) == 0:
            print(f"Unable to find matching results.")
            return
        # 拼接成输入给大模型的内容
        context_text = "\n\n---\n\n".join([doc.page_content for doc, _score in results])
        messages = [
            {'role': 'system', 'content': PROMPT_TEMPLATE.format(context=context_text)},
            {'role': 'user', 'content': f"请回答如下问题:{query}"}
        ]
        print(messages)
        responses = Generation.call(Generation.Models.qwen_max, 
                                    api_key=os.getenv('DASHSCOPE_API_KEY'),
                                    messages=messages, 
                                    result_format='message')
        # 如果你不确定responses的结果,可以打印处理
        # print(responses.output)
        sources = [doc.metadata.get("source", None) for doc, _score in results]
        if responses.status_code == HTTPStatus.OK:
            whole_message = responses.output["choices"][0]["message"]["content"]
        else:
            whole_message = "error"
            print('Failed request_id: %s, status_code: %s, code: %s, message:%s' %
                (responses.request_id, responses.status_code, responses.code,
                responses.message))
        formatted_response = f"Response: {whole_message}\nSources: {sources}"
        print(formatted_response)

参考链接

langchain-rag-tutorial

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

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

相关文章

P8772 [蓝桥杯 2022 省 A] 求和

题目描述: 解题思路: 首先这题我们可以直接用两个for循环嵌套来控制两个变量来求值,但是这样做时间复杂度高。这里我们用到了一个前缀和差的方法。通过for循环变量第一个变量,用和差的方法的到第二个量,这样就只用了一…

Flux Tools 结构简析

Flux Tools 结构简析 BFL 这次一共发布了 Canny、Depth、Redux、Fill 四个 Tools 模型系列,分别对应我们熟悉的 ControlNets、Image Variation(IP Adapter)和 Inpainting 三种图片条件控制方法。虽然实现功能是相同的,但是其具体…

什么是芯片电阻

有人把Chip Resistor翻译成“芯片电阻”,我觉得翻译成“贴片电阻”或“片状电阻”更合适。有些厂商也称之为”电阻片”,英文写作Resistor Chip。比如:Thick film resistor chips(厚膜电阻片)、Thin film resistor chip…

【Linux】深入理解进程信号机制:信号的产生、捕获与阻塞

🎬 个人主页:谁在夜里看海. 📖 个人专栏:《C系列》《Linux系列》《算法系列》 ⛰️ 时间不语,却回答了所有问题 目录 📚前言 📚一、信号的本质 📖1.异步通信 📖2.信…

模具生产过程中的标签使用流程图

①NFC芯片嵌入周转筐,通过读卡器读取CK_Label_v3的数据,并将这些信息上传至服务器进行存储; ②服务器随后与客户的WMS(仓库管理系统)进行交互,记录和同步注塑机的原始数据; ③当周转筐内的模具…

【AIGC安全】CCF-CV企业交流会直播回顾:探寻AI安全治理,共筑可信AI未来

文章目录 一、活动背景:AI技术快速发展与安全治理需求迫切二、论坛内容金耀辉:智能共生时代:平衡生成式AI的创新与风险何延哲:人工智能安全检测评估的逻辑和要点谢洪涛:面向特定人物深度伪造视频的主动防御与被动检测技…

Cesium 无人机航线规划(区域航线)

区域航线,即划定一片区域一键巡查 这里选择点几个点,形成的区域内计算规划航线

【SH】Ubuntu Server 24搭建Web服务器访问Python程序研发笔记

文章目录 说个问题写个方案一、安装Ubuntu Server二、安装Web服务器采用Nginx服务器 三、安装Python及依赖创建项目虚拟环境 四、安装Python Web框架采用Flask框架创建和运行Flask应用(以后的重点) 五、安装WSGI服务器采用Gunicorn 六、配置Nginx七、验证…

SpringBoot如何实现缓存预热?

缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。 那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系统呢? 实现…

贪心算法 part01

class Solution { public:int maxSubArray(vector<int>& nums) {int result INT32_MIN;int count 0;for (int i 0; i < nums.size(); i) {count nums[i];if (count > result) { // 取区间累计的最大值&#xff08;相当于不断确定最大子序终止位置&#xff…

Redis应用—6.热key探测设计与实践

大纲 1.热key引发的巨大风险 2.以往热key问题怎么解决 3.热key进内存后的优势 4.热key探测关键指标 5.热key探测框架JdHotkey的简介 6.热key探测框架JdHotkey的组成 7.热key探测框架JdHotkey的工作流程 8.热key探测框架JdHotkey的性能表现 9.关于热key探测框架JdHotke…

海外招聘丨卢森堡大学—人工智能和机器学习中的 PI 用于图像分析

雇主简介 卢森堡大学立志成为欧洲最受推崇的大学之一&#xff0c;具有鲜明的国际化、多语言和跨学科特色。 她促进研究和教学的相互影响&#xff0c;与国家息息相关&#xff0c;因其在特定领域的研究和教学而闻名于世&#xff0c;并成为当代欧洲高等教育的创新典范。 她的核…

SSM虾米音乐项目6--后台专辑模块的修改和删除

删除操作 删除的前端界面 删除的前端代码 <button data-toggle"button" class"btn btn-sm btn-warning" aid"${album.aid}" pic"${album.pic}"> 删除 </button></td> 点击删除按钮&#xff0c;会调用JS中的AJAX请…

【潜意识Java】了解并详细分析Java与AIGC的结合应用和使用方式

目录 一、AIGC技术概述 二、Java与AIGC结合的价值 三、实现Java与AIGC结合&#xff1a;基于OpenAI的API进行智能文本生成 1. 环境准备 2. Java代码实现 3. 代码解析 4. 运行效果 四、进一步优化与扩展 五、总结 随着人工智能&#xff08;AI&#xff09;的飞速发展&…

基于容器的云原生,让业务更自由地翱翔云端

无论是要构建一个应用或开发一个更庞大的解决方案&#xff0c;在技术选型时&#xff0c;技术的开放性和可移植性已经成为很多企业优先考虑的问题之一。毕竟没人希望自己未来的发展方向和成长速度被自己若干年前选择使用的某项技术所限制或拖累。 那么当你的业务已经上云&#x…

二叉树_堆

目录 一. 树(非线性结构&#xff09; 1.1 树的概念与结构 1.2 树的表示 二. 二叉树 2.1 二叉树的概念与结构 2.2 特殊的二叉树 2.3 二叉树的存储结构 三. 实现顺序结构的二叉树 3.1 堆的概念与结构 一. 树(非线性结构&#xff09; 1.1 树的概念与结构 概念&#xff…

linux0.11源码分析第一弹——bootset.s内容

&#x1f680;前言 本系列主要参考的《linux源码趣读》&#xff0c;也结合之前《一个64位操作系统的设计与实现》的内容结合起来进行整理成本系列博客。在这一篇博客对应的是《linux源码趣读》第一~四回 目录 &#x1f680;前言&#x1f3c6;启动后的第一步&#x1f4c3;启动区…

设计模式之桥接模式:抽象与实现之间的分离艺术

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 桥接模式概述与角色组成 想象一下你家里的电视遥控器&#xff0c;无论是索尼还是三星的电视机&#xff0c;遥控器的按键功能都差不多&#xff1…

【从零开始入门unity游戏开发之——C#篇17】C#面向对象的封装——类(Class)和对象、成员变量和访问修饰符、成员方法

文章目录 一、类和对象1、什么是类和对象&#xff1f;2、例子说明2.1 例子1&#xff1a;(1) **类的定义&#xff1a;**(2) **创建对象&#xff1a;**(3) **类和对象的关系&#xff1a;** 2.2 例子2&#xff1a;**类的比喻&#xff1a;****对象的比喻&#xff1a;**代码实例&…

在Ubuntu 22.04 LTS中使用PyTorch深度学习框架并调用多GPU时遇到indexSelectLargeIndex相关的断言失败【笔记】

在Ubuntu 22.04 LTS系统中&#xff0c;已安装配置好CUDA 12.4、cuDNN 9.1.1以及PyTorch环境 export CUDA_VISIBLE_DEVICES0,1,2,3,4,5,6,7 在PyTorch深度学习框架训练调用多GPU时&#xff0c;提示 indexSelectLargeIndex: block: [x, 0, 0], thread: [x, 0, 0] Assertion src…