跟随《动手学大模型应用开发》学习大模型开发,欢迎大家一起交流。本篇仅摘录部分内容作为个人学习笔记,无意侵权,侵删。
复习基础知识
残差
在深度学习中,网络层数增多一般会伴着下面几个问题:
- 计算资源的消耗 - GPU集群来解决
- 模型容易过拟合 - Data Augmentation、Dropout、正则化 Regularization、Early Stopping
- 梯度消失/梯度爆炸问题的产生 - Batch Normalization、残差、Weight Initialization、Gradient Clipping
随着网络层数的增加,网络发生了退化(degradation)的现象:随着网络层数的增多,训练集loss逐渐下降,然后趋于饱和,当你再增加网络深度的话,训练集loss反而会增大。注意这并不是过拟合,因为在过拟合中训练loss是一直减小的。
从信息论的角度讲,由于DPI(数据处理不等式)的存在,在前向传输的过程中,随着层数的加深,Feature Map包含的图像信息会逐层减少,而ResNet的直接映射的加入,保证了 l+1 层的网络一定比 l 层包含更多的图像信息。
ResNet 中引入了残差连接,即每个残差块的输入会直接加到输出上,形成了一个直接的映射。这样做的好处是,在进行特征变换时,可以保留原始输入的信息,从而避免了信息的丢失。因此,即使网络变深,每一层都可以包含更多的原始图像信息,使得网络更容易学习到有效的特征表示。
综上所述,残差连接的加入使得深层网络中的每一层都可以包含更多的图像信息,从而提高了网络的性能和训练效率,有效缓解了梯度消失和特征信息丢失的问题。
RNN 循环神经网络
引用来源:一文搞懂RNN(循环神经网络)基础篇 - 知乎 (zhihu.com)
某些任务需要能够更好的处理序列的信息,即前面的输入和后面的输入是有关系的。 当我们在理解一句话意思时,孤立的理解这句话的每个词是不够的,我们需要处理这些词连接起来的整个序列; 当我们处理视频的时候,我们也不能只单独的去分析每一帧,而要分析这些帧连接起来的整个序列。
LSTM 长短期记忆递归神经网络
引用来源:LSTM - 长短期记忆递归神经网络 - 知乎 (zhihu.com)
RNN的硬伤 -> 都可被LSTM解决:1. 长期依赖问题:Sea is (blue): easy ; 我一直呆在中国......我会说一口流利的 (中文): hard. 2. 梯度消失问题(神经网络的权重/偏置梯度极小,导致神经网络参数调整速率急剧下降)和梯度爆炸问题(神经网络的权重/偏置梯度极大,导致神经网络参数调整幅度过大,矫枉过正)。相信大家都看过一个著名的鸡汤, (0.99)^365 和 (1.01)^365 的对比。实际上,这个鸡汤非常好的描述了梯度问题的本质:对于任意信息递归使用足够多次同样的计算,都会导致极大或极小的结果。
长短期记忆——只有一部分的信息需要长期的记忆,而有的信息可以不记下来。同时,我们还需要一套机制可以动态的处理神经网络的“记忆”,因为有的信息可能一开始价值很高,后面价值逐渐衰减,这时候我们也需要让神经网络学会“遗忘”特定的信息。
LLM 介绍
研究人员发现,随着语言模型规模的扩大(增加模型大小或使用更多数据),模型展现出了一些惊人的能力,在各种任务中的表现均显著提升。这一发现标志着大型语言模型(LLM)时代的开启。借助于海量无标注数据的训练,获得可以适用于大量下游任务的大模型(单模态或者多模态)。这样,多个应用可以只依赖于一个或少数几个大模型进行统一建设。
LLM 采用了预训练和微调的学习方法。首先在大规模文本数据上进行预训练(无标签数据),学习通用的语言表示和知识。然后通过微调(有标签数据)适应特定任务,从而在各种 NLP 任务中表现出色。
GPT 模型的基本原则是通过语言建模将世界知识压缩到仅解码器 (decoder-only) 的 Transformer 模型中,这样它就可以恢复(或记忆)世界知识的语义,并充当通用任务求解器。它能够成功的两个关键点:
- 训练能够准确预测下一个单词的 decoder-only 的 Transformer 语言模型
- 扩展语言模型的大小
LLM:涌现能力-在大型模型中特别突出-量变引起质变。介绍三个 LLM 典型的涌现能力:
-
上下文学习:上下文学习能力是由 GPT-3 首次引入的。这种能力允许语言模型在提供自然语言指令或多个任务示例的情况下,通过理解上下文并生成相应输出的方式来执行任务,而无需额外的训练或参数更新。
-
指令遵循:通过使用自然语言描述的多任务数据进行微调,也就是所谓的
指令微调
。LLM 被证明在使用指令形式化描述的未见过的任务上表现良好。这意味着 LLM 能够根据任务指令执行任务,而无需事先见过具体示例,展示了其强大的泛化能力。 -
逐步推理:小型语言模型通常难以解决涉及多个推理步骤的复杂任务,例如数学问题。然而,LLM 通过采用
思维链(CoT, Chain of Thought)
推理策略,利用包含中间推理步骤的提示机制来解决这些任务,从而得出最终答案。据推测,这种能力可能是通过对代码的训练获得的。
检索增强生成(RAG)
RAG 对 LLM 的帮助
Retrieval-Augmented Generation 架构巧妙地整合了从庞大知识库中检索到的相关信息,并以此为基础,指导大型语言模型生成更为精准的答案,从而显著提升了回答的准确性与深度。在提升大语言模型效果中,RAG 和 微调(Finetune)是两种主流的方法。RAG不需要重新训练,微调需要。
-
信息偏差/幻觉: LLM 有时会产生与客观事实不符的信息,导致用户接收到的信息不准确。RAG 通过检索数据源,辅助模型生成过程,确保输出内容的精确性和可信度,减少信息偏差。
-
知识更新滞后性: LLM 基于静态的数据集训练,这可能导致模型的知识更新滞后,无法及时反映最新的信息动态。RAG 通过实时检索最新数据,保持内容的时效性,确保信息的持续更新和准确性。
-
内容不可追溯: LLM 生成的内容往往缺乏明确的信息来源,影响内容的可信度。RAG 将生成内容与检索到的原始资料建立链接,增强了内容的可追溯性,从而提升了用户对生成内容的信任度。
-
领域专业知识能力欠缺: LLM 在处理特定领域的专业知识时,效果可能不太理想,这可能会影响到其在相关领域的回答质量。RAG 通过检索特定领域的相关文档,为模型提供丰富的上下文信息,从而提升了在专业领域内的问题回答质量和深度。
-
推理能力限制: 面对复杂问题时,LLM 可能缺乏必要的推理能力,这影响了其对问题的理解和回答。RAG 结合检索到的信息和模型的生成能力,通过提供额外的背景知识和数据支持,增强了模型的推理和理解能力。
-
应用场景适应性受限: LLM 需在多样化的应用场景中保持高效和准确,但单一模型可能难以全面适应所有场景。RAG 使得 LLM 能够通过检索对应应用场景数据的方式,灵活适应问答系统、推荐系统等多种应用场景。
-
长文本处理能力较弱: LLM 在理解和生成长篇内容时受限于有限的上下文窗口,且必须按顺序处理内容,输入越长,速度越慢。RAG 通过检索和整合长文本信息,强化了模型对长上下文的理解和生成,有效突破了输入长度的限制,同时降低了调用成本,并提升了整体的处理效率。
RAG 工作流程
RAG 是一个完整的系统,其工作流程可以简单地分为数据处理、检索、增强和生成四个阶段:
- 数据处理阶段
- 对原始数据进行清洗和处理。
- 将处理后的数据转化为检索模型可以使用的格式。
- 将处理后的数据存储在对应的数据库中。
- 检索阶段
- 将用户的问题输入到检索系统中,从数据库中检索相关信息。
- 增强阶段
- 对检索到的信息进行处理和增强,以便生成模型可以更好地理解和使用。
- 生成阶段
- 将增强后的信息输入到生成模型中,生成模型根据这些信息生成答案。
LangChain 框架
旨在帮助开发者们快速构建基于大型语言模型的端到端应用程序或工作流程。
LangChian 作为一个大语言模型开发框架,可以将 LLM 模型(对话模型、embedding 模型等)、向量数据库、交互层 Prompt、外部知识、外部代理工具整合到一起,进而可以自由构建 LLM 应用。 LangChain 主要由以下 6 个核心组件组成:
- 模型输入/输出(Model I/O):与语言模型交互的接口
- 数据连接(Data connection):与特定应用程序的数据进行交互的接口
- 链(Chains):将组件组合实现端到端应用。比如后续我们会将搭建
检索问答链
来完成检索问答。 - 记忆(Memory):用于链的多次运行之间持久化应用程序状态;
- 代理(Agents):扩展模型的推理能力。用于复杂的应用的调用序列;
- 回调(Callbacks):扩展模型的推理能力。用于复杂的应用的调用序列;
在开发过程中,我们可以根据自身需求灵活地进行组合。
LLM 开发流程
开发以大语言模型为功能核心、通过大语言模型的强大理解能力和生成能力、结合特殊的数据或业务逻辑来提供独特功能的应用称为大模型开发。大模型开发中,我们一般不会去大幅度改动模型,而是将大模型作为一个调用工具,通过 Prompt Engineering、数据工程、业务逻辑分解等手段来充分发挥大模型能力,适配应用任务,而不会将精力聚焦在优化模型本身上。
大语言模型的两个核心能力:指令遵循
与文本生成
提供了复杂业务逻辑的简单平替方案。
传统的 AI 开发
:首先需要将非常复杂的业务逻辑依次拆解,对于每一个子业务构造训练数据与验证数据,对于每一个子业务训练优化模型,最后形成完整的模型链路来解决整个业务逻辑。大模型开发
:用 Prompt Engineering 来替代子模型的训练调优,通过 Prompt 链路组合来实现业务逻辑,用一个通用大模型 + 若干业务 Prompt 来解决任务,从而将传统的模型训练调优转变成了更简单、轻松、低成本的 Prompt 设计调优。
同时,在评估思路上,大模型开发与传统 AI 开发也有质的差异。
传统 AI 开发
:需要首先构造训练集、测试集、验证集,通过在训练集上训练模型、在测试集上调优模型、在验证集上最终验证模型效果来实现性能的评估。大模型开发
:流程更为灵活和敏捷。从实际业务需求出发构造小批量验证集,设计合理 Prompt 来满足验证集效果。然后,将不断从业务逻辑中收集当下 Prompt 的 Bad Case,并将 Bad Case 加入到验证集中,针对性优化 Prompt,最后实现较好的泛化效果。
-
确定目标。在进行开发前,我们首先需要确定开发的目标,即要开发的应用的应用场景、目标人群、核心价值。对于个体开发者或小型开发团队而言,一般应先设定最小化目标,从构建一个 MVP(最小可行性产品)开始,逐步进行完善和优化。
-
设计功能。在确定开发目标后,需要设计本应用所要提供的功能,以及每一个功能的大体实现逻辑。虽然我们通过使用大模型来简化了业务逻辑的拆解,但是越清晰、深入的业务逻辑理解往往也能带来更好的 Prompt 效果。同样,对于个体开发者或小型开发团队来说,首先要确定应用的核心功能,然后延展设计核心功能的上下游功能;例如,我们想打造一款个人知识库助手,那么核心功能就是结合个人知识库内容进行问题的回答,那么其上游功能的用户上传知识库、下游功能的用户手动纠正模型回答就是我们也必须要设计实现的子功能。
-
搭建整体架构。目前,绝大部分大模型应用都是采用的特定数据库 + Prompt + 通用大模型的架构。我们需要针对我们所设计的功能,搭建项目的整体架构,实现从用户输入到应用输出的全流程贯通。一般来说,我们推荐基于 LangChain 框架进行开发。LangChain 提供了 Chain、Tool 等架构的实现,我们可以基于 LangChain 进行个性化定制,实现从用户输入到数据库再到大模型最后输出的整体架构连接。
-
搭建数据库。个性化大模型应用需要有个性化数据库进行支撑。由于大模型应用需要进行向量语义检索,一般使用诸如 Chroma 的向量数据库。在该步骤中,我们需要收集数据并进行预处理,再向量化存储到数据库中。数据预处理一般包括从多种格式向纯文本的转化,例如 PDF、MarkDown、HTML、音视频等,以及对错误数据、异常数据、脏数据进行清洗。完成预处理后,需要进行切片、向量化构建出个性化数据库。
-
Prompt Engineering。优质的 Prompt 对大模型能力具有极大影响,我们需要逐步迭代构建优质的 Prompt Engineering 来提升应用性能。在该步中,我们首先应该明确 Prompt 设计的一般原则及技巧,构建出一个来源于实际业务的小型验证集,基于小型验证集设计满足基本要求、具备基本能力的 Prompt。
-
验证迭代。验证迭代在大模型开发中是极其重要的一步,一般指通过不断发现 Bad Case 并针对性改进 Prompt Engineering 来提升系统效果、应对边界情况。在完成上一步的初始化 Prompt 设计后,我们应该进行实际业务测试,探讨边界情况,找到 Bad Case,并针对性分析 Prompt 存在的问题,从而不断迭代优化,直到达到一个较为稳定、可以基本实现目标的 Prompt 版本。
-
前后端搭建。完成 Prompt Engineering 及其迭代优化之后,我们就完成了应用的核心功能,可以充分发挥大语言模型的强大能力。接下来我们需要搭建前后端,设计产品页面,让我们的应用能够上线成为产品。前后端开发是非常经典且成熟的领域,此处就不再赘述,我们采用 Gradio 和 Streamlit,可以帮助个体开发者迅速搭建可视化页面实现 Demo 上线。
-
体验优化。在完成前后端搭建之后,应用就可以上线体验了。接下来就需要进行长期的用户体验跟踪,记录 Bad Case 与用户负反馈,再针对性进行优化即可。
搭建 LLM 项目的流程简析(以知识库助手为例)
以下我们将结合本实践项目与上文的整体流程介绍,简要分析知识库助手项目开发流程:
步骤一:项目规划与需求分析
1.项目目标:基于个人知识库的问答助手
2.核心功能
- 将爬取并总结的 MarkDown 文件及用户上传文档向量化,并创建知识库;
- 选择知识库,检索用户提问的知识片段;
- 提供知识片段与提问,获取大模型回答;
- 流式回复;
- 历史对话记录
3.确定技术架构和工具
- 框架:LangChain
- Embedding 模型:GPT、智谱、M3E
- 数据库:Chroma
- 大模型:GPT、讯飞星火、文心一言、GLM 等
- 前后端:Gradio 和 Streamlit
步骤二:数据准备与向量知识库构建
本项目实现原理如下图所示(图片来源):加载本地文档 -> 读取文本 -> 文本分割 -> 文本向量化 -> question 向量化 -> 在文本向量中匹配出与问句向量最相似的 top k 个 -> 匹配出的文本作为上下文和问题一起添加到 Prompt 中 -> 提交给 LLM 生成回答。
1.收集和整理用户提供的文档
用户常用文档格式有 PDF、TXT、MD 等,首先,我们可以使用 LangChain 的文档加载器模块方便地加载用户提供的文档,或者使用一些成熟的 Python 包进行读取。
由于目前大模型使用 token 的限制,我们需要对读取的文本进行切分,将较长的文本切分为较小的文本,这时一段文本就是一个单位的知识。
2.将文档词向量化
使用文本嵌入(Embeddings)技术
对分割后的文档进行向量化,使语义相似的文本片段具有接近的向量表示。然后,存入向量数据库,完成 索引(index)
的创建。
利用向量数据库对各文档片段进行索引,可以实现快速检索。
3.将向量化后的文档导入 Chroma 知识库,建立知识库索引
Langchain 集成了超过 30 个不同的向量数据库。Chroma 数据库轻量级且数据存储在内存中,这使得它非常容易启动和开始使用。
将用户知识库内容经过 Embedding 存入向量数据库,然后用户每一次提问也会经过 Embedding,利用向量相关性算法(例如余弦算法)找到最匹配的几个知识库片段,将这些知识库片段作为上下文,与用户问题一起作为 Prompt 提交给 LLM 回答。
步骤三:大模型集成与 API 连接
- 集成 GPT、星火、文心、GLM 等大模型,配置 API 连接。
- 编写代码,实现与大模型 API 的交互,以便获取问题回答。
步骤四:核心功能实现
- 构建 Prompt Engineering,实现大模型回答功能,根据用户提问和知识库内容生成回答。
- 实现流式回复,允许用户进行多轮对话。
- 添加历史对话记录功能,保存用户与助手的交互历史。
步骤五:核心功能迭代优化
- 进行验证评估,收集 Bad Case。
- 根据 Bad Case 迭代优化核心功能实现。
步骤六:前端与用户交互界面开发
- 使用 Gradio 和 Streamlit 搭建前端界面。
- 实现用户上传文档、创建知识库的功能。
- 设计用户界面,包括问题输入、知识库选择、历史记录展示等。
步骤七:部署测试与上线
- 部署问答助手到服务器或云平台,确保可在互联网上访问。
- 进行生产环境测试,确保系统稳定。
- 上线并向用户发布。
步骤八:维护与持续改进
- 监测系统性能和用户反馈,及时处理问题。
- 定期更新知识库,添加新的文档和信息。
- 收集用户需求,进行系统改进和功能扩展。
整个流程将确保项目从规划、开发、测试到上线和维护都能够顺利进行,为用户提供高质量的基于个人知识库的问答助手。