datawhale大模型应用开发夏令营学习笔记一

news2024/10/6 12:24:13

参考自

  • 基于LangChain+LLM的本地知识库问答:从企业单文档问答到批量文档问答
  • datawhale的llm-universe

作者现在在datawhale夏令营的大模型应用开发这个班中,作为一个小白,为了能为团队做出一点贡献,现在就要开始学习怎么使用langchain的api来实现基本的功能!

随便谈谈

由于科大讯飞的星火杯已经为我们提供了 星火大模型 的API,我也不好进行模型调参,现在能做的就是开展prompt工程,结合RAG,给到模型优质的输入信息,帮助它正确回答我提出的疑问和要求。我首先学的就是这两个。

我一开始的想法是做本地知识库,那么模型是怎么知道这些知识呢?我们知道一个人的知识储备,可以来自他的积累,也可以来自他现在手里正翻着的书(或者当场百度:))。大模型也一样。我们可以在训练大模型时让他学习庞大的知识,也可以在提问一些小众知识时附加可参考的内容(比如在询问一个rust的第三方库怎么用时,顺带把这个库的文档文本一起给他,让他参考这些文档进行回答)

  • 参数知识:在训练期间学习到的知识,隐式存储在神经网络的权重中。
  • 非参数知识:存储在外部知识源中,例如向量数据库。

当然,直接传整篇文档的方式比较粗暴,可以事先将全篇文档放入向量数据库或知识图谱(统称为知识库)。那么一次问答的过程为:首先用户提问请回答我的xx问题,然后大模型根据用户的提问去知识库中匹配并获取相关的参考资料,然后再将用户输入改为根据 xxxxx 这些参考资料,请回答我的xx问题并传入大模型。

一步步基于langchain实现讯飞大模型的调用和RAG

主要参考自datawhale的教程https://github.com/datawhalechina/llm-universe/tree/main/notebook,确实比较详细
我实操的大概过程为:

  1. 调用星火大模型
  2. 读取Markdown或pdf作为本地知识,并解析出其中的文本,并进行数据清洗
  3. 对解析出的文本进行文本切分。因为单个文档的长度往往会超过模型支持的上下文,导致检索得到的知识太长超出模型的处理能力
  4. 调用星火的文本向量化接口
  5. 使用Chroma存储向量并检索,即建立本地知识库
  6. 使用template,并使用langchain的LCEL语法实现一条链式处理,该链将获取输入变量,将这些变量传递给提示模板以创建提示,将提示传递给语言模型,然后通过(可选)输出解析器传递输出。
  7. 将"知识库检索"这一过程加入处理链中,实现大模型链接本地知识库
  8. 基于Memory模块让大模型能使用到历史对话,实现带上下文的对话
  9. streamlit的教学我没看,目前考虑使用Gradio

以上过程均基于langchain的api。
我这里只放一些重点内容,对详细过程感兴趣的同学可以跟着教程继续学习,我就不粘贴一遍教程里的代码了

RAG和微调的对比

特征比较RAG微调
知识更新直接更新检索知识库,无需重新训练。信息更新成本低,适合动态变化的数据。通常需要重新训练来保持知识和数据的更新。更新成本高,适合静态数据。
外部知识擅长利用外部资源,特别适合处理文档或其他结构化/非结构化数据库。将外部知识学习到 LLM 内部。
数据处理对数据的处理和操作要求极低。依赖于构建高质量的数据集,有限的数据集可能无法显著提高性能。
模型定制侧重于信息检索和融合外部知识,但可能无法充分定制模型行为或写作风格。可以根据特定风格或术语调整 LLM 行为、写作风格或特定领域知识。
可解释性可以追溯到具体的数据来源,有较好的可解释性和可追踪性。黑盒子,可解释性相对较低。
计算资源需要额外的资源来支持检索机制和数据库的维护。依赖高质量的训练数据集和微调目标,对计算资源的要求较高。
推理延迟增加了检索步骤的耗时单纯 LLM 生成的耗时
降低幻觉通过检索到的真实信息生成回答,降低了产生幻觉的概率。模型学习特定领域的数据有助于减少幻觉,但面对未见过的输入时仍可能出现幻觉。
伦理隐私检索和使用外部数据可能引发伦理和隐私方面的问题。训练数据中的敏感信息需要妥善处理,以防泄露。

LangChain

封装了很多模型的调用方式以及工具比如chain、memory等
https://python.langchain.com/v0.2/docs/integrations/llms/

Prompt

在 ChatGPT 推出并获得大量应用之后,Prompt 开始被推广为给大模型的所有输入。即,我们每一次访问大模型的输入为一个 Prompt,而大模型给我们的返回结果则被称为 Completion。

Temperature

LLM 生成是具有随机性的,我们一般可以通过控制 temperature 参数来控制 LLM 生成结果的随机性与创造性。

Temperature 一般取值在 0~1 之间,当取值较低接近 0 时,预测的随机性会较低,产生更保守、可预测的文本,不太可能生成意想不到或不寻常的词。当取值较高接近 1 时,预测的随机性会较高,所有词被选择的可能性更大,会产生更有创意、多样化的文本,更有可能生成不寻常或意想不到的词。

对于不同的问题与应用场景,我们可能需要设置不同的 temperature。例如,在个人知识库助手项目中,我们一般将 temperature 设置为 0,从而保证助手对知识库内容的稳定使用,规避错误内容、模型幻觉;在产品智能客服、科研论文写作等场景中,我们同样更需要稳定性而不是创造性;但在个性化 AI、创意营销文案生成等场景中,我们就更需要创意性,从而更倾向于将 temperature 设置为较高的值。

System Prompt

在使用 ChatGPT API 时,你可以设置两种 Prompt:一种是 System Prompt,该种 Prompt 内容会在整个会话过程中持久地影响模型的回复,且相比于普通 Prompt 具有更高的重要性;另一种是 User Prompt,这更偏向于我们平时提到的 Prompt,即需要模型做出回复的输入。
System Prompt 一般在一个会话中仅有一个。在通过 System Prompt 设定好模型的人设或是初始设置后,我们可以通过 User Prompt 给出模型需要遵循的指令。
例如,当我们需要一个幽默风趣的个人知识库助手,并向这个助手提问我今天有什么事时,可以构造如下的 Prompt:

{
    "system prompt": "你是一个幽默风趣的个人知识库助手,可以根据给定的知识库内容回答用户的提问,注意,你的回答风格应是幽默风趣的",
    "user prompt": "我今天有什么事务?"
}

Prompt工程

可以理解为使用结构化的提问方式,向模型传入更详细的提问内容,引导大模型进行更精确和正确的回答
具体可参考https://github.com/datawhalechina/llm-universe/blob/main/notebook/C2%20%E4%BD%BF%E7%94%A8%20LLM%20API%20%E5%BC%80%E5%8F%91%E5%BA%94%E7%94%A8/3.%20Prompt%20Engineering.ipynb

此次开发用到的官方文档

  • langchain中使用sparkllm
  • langchain中使用spark的文本向量化
  • langchain中解析文件中的文本
  • langchain中使用Chroma

还是贴个代码

知识库为一篇Markdown https://github.com/sunface/rust-course/blob/main/src/advance/macro.md
这里的代码参考datawhale的代码,只实现了链接知识库和结合上下文对话,没有用到LCEL。深入源码以及看官方文档后发现,LangChain非常多的API弃用或将被弃用,API改动这么频繁,而且有些API都不好在IDE里跳到源码,难怪langchain的争议这么大。

import os
import re
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.llms.sparkllm import SparkLLM
from langchain_community.embeddings import SparkLLMTextEmbeddings
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain.prompts.chat import ChatPromptTemplate,SystemMessagePromptTemplate,HumanMessagePromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain

# 通过环境变量的方式设置秘钥,具体的key-value可以在IDE内点进源码查看
def load_env():
   # 星火认知大模型Spark Max的URL值,其他版本大模型URL值请前往文档(https://www.xfyun.cn/doc/spark/Web.html)查看
   #星火认知大模型调用秘钥信息,请前往讯飞开放平台控制台(https://console.xfyun.cn/services/bm35)查看
   #星火认知大模型Spark Max的domain值,其他版本大模型domain值请前往文档(https://www.xfyun.cn/doc/spark/Web.html)查看
   os.environ["IFLYTEK_SPARK_API_URL"] = "wss://spark-api.xf-yun.com/v3.5/chat"
   os.environ["IFLYTEK_SPARK_API_KEY"] = ""
   os.environ["IFLYTEK_SPARK_API_SECRET"] = ""
   os.environ["IFLYTEK_SPARK_APP_ID"] = ""
   os.environ["IFLYTEK_SPARK_LLM_DOMAIN"] = "generalv3.5"
   # 文本向量化
   os.environ["SPARK_APP_ID"] = ""
   os.environ["SPARK_API_KEY"] =  ""
   os.environ["SPARK_API_SECRET"] = ""

# 使用UnstructuredMarkdownLoader读取时似乎不能读emoji
# 此时需要预先处理文件,再使用API解析无emoji的Markdown文件
def remove_emojis(text):
   try:
       co = re.compile(u'[\U00010000-\U0010ffff]')
   except re.error:
       co = re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]')
   no_emojis_text = co.sub(r'', text)
   return no_emojis_text

# 长文本切分为多个小文档
def text_split(doc):
   # 知识库中单段文本长度
   CHUNK_SIZE = 500
   # 知识库中相邻文本重合长度
   OVERLAP_SIZE = 50
   # 使用递归字符文本分割器
   text_splitter = RecursiveCharacterTextSplitter(
       chunk_size=CHUNK_SIZE,
       chunk_overlap=OVERLAP_SIZE
   )
   return text_splitter.split_documents(doc)

def prepare_knowledge():
   # 此篇文章无emoji
   files = ["macro.md"]
   md_pages = []
   for f in files:
       loader = UnstructuredMarkdownLoader(f)
       md_page = loader.load()
       # 打印这个文档来自哪个的文件名
       # source = md_page.metadata['source']
       # print(source)
       # 删除无必要的连续换行
       md_page[0].page_content = md_page[0].page_content.replace('\n\n', '\n')
       md_pages.extend(md_page)
   # 文本切分
   docs = text_split(md_pages)
   # 实例化星火文本向量化接口的调用工具
   embeddings = SparkLLMTextEmbeddings()
   # 可以直接调用来向量化文本
   # embeddings.aembed_query(text)/aembed_documents(doc)
   # 初始化一个Chroma向量数据库,让Chroma将文档向量化
   # 并将数据库文件和向量数据保存到文件夹中
   vectordb = Chroma.from_documents(
       documents=docs,
       embedding=embeddings,
       persist_directory='./vector'
   )
   vectordb.persist()

if __name__ == '__main__':
   # 设置环境变量
   load_env()
   # 实例化星火大模型调用工具
   llm = SparkLLM(temperature=0.95)
   # 初始化知识库
   prepare_knowledge()
   # 加载本地知识库
   embeddings = SparkLLMTextEmbeddings()
   vector_db = Chroma(persist_directory="./vector", embedding_function=embeddings)
   # 对话历史记录
   memory = ConversationBufferMemory(
       memory_key="chat_history",
       return_messages=True,  # 将以消息列表的形式返回聊天记录,而不是单个字符串
   )
   # 模版
   system_template = """使用提供的上下文和聊天记录回答用户的问题。不知道也不要编造答案,回答尽量简要。并且总是在回答的最后说“谢谢你的提问!”
   ----------------
   CONTEXT:
   {context}

   CHAT HISTORY:
   {chat_history}

   USER QUESTION:
   {question}
   """
   messages = [
       SystemMessagePromptTemplate.from_template(system_template),
       HumanMessagePromptTemplate.from_template("{question}")
   ]
   qa_prompt = ChatPromptTemplate.from_messages(messages)
   # 创建问答链,为其添加检索知识库和历史记录的功能
   qa = ConversationalRetrievalChain.from_llm(
       llm,
       retriever=vector_db.as_retriever(),
       combine_docs_chain_kwargs={'prompt': qa_prompt},
       memory=memory
   )
   print("第一次问答:")
   question = "我可以在你这里学习到关于提示工程的知识吗?"
   result = qa({"question": question})
   print(result['answer'])
   print("第二次问答:")
   question = "为什么?"
   result = qa({"question": question})
   print(result['answer'])

在这里插入图片描述
在这里插入图片描述

由于突然文本向量化接口突然报错,上述代码我这里测试不了构建知识库了。但好在可以加载之前构建好的知识库来测试后面的代码。代码总体应该没问题,之后再看看
Request error: 11202, {‘header’: {‘code’: 11202, ‘message’: ‘licc failed’, ‘sid’: ‘emb000fc090@dx1907452bed6738d882’}}
Request error: 11202, {‘header’: {‘code’: 11202, ‘message’: ‘licc failed’, ‘sid’: ‘emb000ebcb7@dx1907452bf547020882’}}

TODO

  • 怎么持久化以及加载历史信息,这篇博客到时候可以参考:【LangChain】对话式问答(Conversational Retrieval QA)
  • 怎么将知识图谱作为知识库
  • 评测和优化

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

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

相关文章

地下电子标识器探测仪ED8000选型注意事项

ED8000探测仪是一台集成了多频率、多种ID标识器调制模式、高低灵敏度调节、可读写标识器等全功能、高性能电子标识器探测仪。它有着极高的灵敏度,同时具备良好的噪声抑制能力,不仅适合专业测绘人员,普通操作人员也可以轻松掌握。 ED8000可支持模拟电子标…

Android10以上实现获取设备序列号功能

Android10以上实现获取设备唯一标识,目前只支持华为和荣耀设备。实现原理:通过无障碍服务读取序列号界面。 public class DeviceHelper implements Application.ActivityLifecycleCallbacks {static final String TAG "WADQ_DeviceHelper";s…

【ubuntu自启shell脚本】——在ubuntu中如何使用系统自带的启动应用程序设置开机自启自己的本地shell脚本

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、设置开机自启shell脚本1.使用 gnome-session-properties2.测试的shell例程代码 总结 前言 在Ubuntu系统中设置开机自启脚本是一种重要的自动化方法。开机自…

基于docker轻松部署selenium grid环境

做web自动化的同学都知道selenium grid非常好用,但是环境配置特别麻烦,很多人都躺在了环境搭建。那么有没有更简单的方式呢,答案是肯定的,今天我们就用docker来完成它,希望对大家有帮助。 一、环境准备 准备一台 Linu…

《安全大模型技术与市场研究报告》发布,海云安榜上有名

近日,网络安全产业研究机构“数说安全”发布2024《安全大模型技术与市场研究报告》(以下简称“报告”)。 海云安凭借在开发安全领域的优秀业务能力以及在大模型相关技术研究方面的成就得到了认可,入选“安全开发大模型推荐供应商”…

实现点击Button,改变背景颜色(多个按钮互斥显示)

一 功能描述 在界面中,有一组button,现在需要实现下面功能:点击其中一个,改变被点击button的背景颜色。当点击下一个之后,之前点击过的按钮背景颜色还原,当前被点击的button背景色又被改变。效果如下图&…

PowerDsigner的简单使用

目录 1.PowerDesinger 2.PD与navicat的区别: 3.使用 1.PowerDesinger 在实际开发中,数据库的设计会使用专业的建模工具——PowerDesinger (安装及其破解大家搜选相关CSDN博客吧) 2.PD与navicat的区别: navicat是…

Zabbix企业级监控系统

——监控可以帮助我们做什么? 作为一个运维,需要会使用监控系统查看服务器系统性能、应用服务状态和网站流量指标等,利用监控系统的数据去了解网站上线发布的结果和健康状态。 利用一个优秀的监控软件,我们可以: 通过一个友好的…

Kubernetes云原生存储解决方案openebs部署实践-4.0.1版本(helm部署)

Kubernetes云原生存储解决方案openebs部署实践-4.0.1版本(helm部署) 简介 OpenEBS 是一种开源云原生存储解决方案。OpenEBS 可以将 Kubernetes 工作节点可用的任何存储转化为本地或复制的 Kubernetes 持久卷。OpenEBS 帮助应用和平台团队轻松地部署需要…

FlinkCDC-3.1.1 DataStream Source

问题&#xff1a; Caused by: java.lang.ClassNotFoundException: org.apache.flink.table.catalog.ObjectPath 解决&#xff1a; 在poml文件中&#xff0c;导入的flink-table依赖把“ <scope>”去掉 <properties><maven.compiler.source>8</maven.compi…

ffmpeg将多个yuv文件编码为MP4视频文件

一、编码方案 在视频录制时&#xff0c;每一帧保存为一个yuv文件&#xff0c;便于纠错和修改。在编码为MP4文件时&#xff0c;我的方案是将所有yuv文件先转码为单个MP4文件&#xff0c;然后使用ffmpeg的concat功能拼接为完整的视频。 二、shell脚本 #!/bin/bash# 检查参数数量…

自己训练 PaddleOCR

打标工具 https://github.com/Evezerest/PPOCRLabel 感谢这位热心网友提供的标注工具&#xff0c;操作非常的方便 只是这个工具有个小坑get_rotate_crop_image&#xff08;&#xff09; 我的标注数据导出时&#xff0c;很多数据变成倒的 hmmmm, 你管我~ if dst_img_height …

Lesson 47 A cup of coffee

Lesson 47 A cup of coffee 词汇 like v. 喜欢&#xff0c;想要 用法&#xff1a;like 物品 / 人 喜欢……    like 动词ing 喜欢做……&#xff08;习惯性&#xff09;    like to 动词原形 喜欢做……&#xff08;一次性&#xff09; 例句&#xff1a;我喜欢小狗…

【UE5.1】Chaos物理系统基础——03 炸开几何体集

目录 步骤 一、通过径向向量将几何体集炸开 二、优化炸开效果——让破裂的碎块自然下落 三、优化炸开效果——让碎块旋转起来 四、优化炸开效果——让碎块旋转的越来越慢 步骤 一、通过径向向量将几何体集炸开 1. 打开上一篇中&#xff08;【UE5.1】Chaos物理系统基础—…

第3章.中央服务器的物联网模式--规则引擎

规则引擎 规则引擎本质上是物联网事件和需要与这些事件相关联的动作之间的映射。在物联网环境中&#xff0c;事件通常使用传感器生成&#xff0c;所需的动作由执行器采取。本书中用于该图案的符号如下图所示&#xff1a; 图3.6–“规则引擎”模式的符号 一个有趣的类比是将规则…

Vue2前端实现数据可视化大屏全局自适应 Vue实现所有页面自适应 Vue实现自适应所有屏幕

Vue自适应所有屏幕大小,目前页面自适应,尤其是数据可视化大屏的自适应更是案例很多 今天就记录一下使用Vue全局自适应各种屏幕大小的功能 在Vue.js中创建一个数据大屏,并使其能够自适应不同屏幕大小,通常涉及到布局的响应式设计、CSS媒体查询、以及利用Vue的事件系统来处理…

stm8玩耍日记1

写在前面&#xff0c;如题所示&#xff0c;这是一个stm8L051F3的玩耍记录。 环境使用的是IAR for stm8&#xff0c;使用stlink v2作为调试下载器&#xff0c;跟着st中文论坛的一个大佬的教程学习的。 整体配置下来&#xff0c;点亮了led&#xff0c;感觉和stm32的开发差不多&…

电脑鼠标一直转圈圈怎么处理?对症下药,分享6种方法

在使用电脑的过程中&#xff0c;鼠标一直转圈圈是一个常见且令人困扰的问题。这种情况通常意味着系统正在处理某些任务&#xff0c;但如果持续时间过长&#xff0c;可能表明系统存在性能问题或错误。本文将详细探讨鼠标一直转圈圈的常见原因及其解决方法。 摘要 电脑鼠标一直转…

解决Python爬虫开发中的数据输出问题:确保正确生成CSV文件

引言 在大数据时代&#xff0c;爬虫技术成为获取和分析网络数据的重要工具。然而&#xff0c;许多开发者在使用Python编写爬虫时&#xff0c;常常遇到数据输出问题&#xff0c;尤其是在生成CSV文件时出错。本文将详细介绍如何解决这些问题&#xff0c;并提供使用代理IP和多线程…