【DataWhale--大模型应用开发】动手学大模型应用全栈开发

写在前面

  • Datawhale (linklearner.com) 动手学大模型应用全栈开发

  • 学习内容提要:通过学习大模型部署【搭建你的智能编程助手】、大模型检索增强生成(Retrieval Augmented Generation, RAG)实战【搭建你的AI科研助手】、大模型微调实战【搭建你的AI简历助手】,掌握大模型应用全栈开发

背景知识

概念

  • 为了对人类语言的内在规律进行建模,研究者们提出使用语言模型(language model)来准确预测词序列中 下一个词 或者 缺失的词 的概率

  • 大语言模型(Large Language Model, LLM):基于“扩展法则”(Scaling Law),即通过增加模型参数或训练数据,可以提升下游任务的性能,同时具有小模型不具有的“涌现能力”(Emergent Abilities)。代表性工作:GPT-3、ChatGPT、Claude、Llama

构建过程

  • 大模型的构建过程可以分为**预训练(Pretraining)、有监督微调(Supervised Fine-tuning, SFT)、基于人类反馈的强化学习对齐**(Reinforcement Learning from Human Feedback, RLHF)三个阶段
预训练
  • 预训练指使用海量的数据进行模型参数的初始学习,旨在为模型参数寻找到一个优质的“起点”
有监督微调
  • 经过大规模预训练后,模型已经具备较强的模型能力,能够编码丰富的世界知识
  • 但是由于预训练任务形式所限,这些模型更擅长于文本补全,并不适合直接解决具体的任务
  • 该方法利用成对的任务输入与预期输出数据,训练模型学会以问答的形式解答问题,从而解锁其任务解决潜能
  • 指令微调并非无中生有地传授新知,而是更多地扮演着催化剂的角色,激活模型内在的潜在能力,而非单纯地灌输信息
基于人类反馈的强化学习对齐–RLHF
  • 核心:构建一个反映人类价值观的奖励模型(Reward Model)
  • 这一模型的训练依赖于专家对模型多种输出的偏好排序,通过偏好数据训练出的奖励模型能够有效评判模型输出的质量
示例:开源大模型Llama-2-Chat训练过程
  • 起始:利用公开数据进行预训练,获得Llama-2
  • 此后:通过有监督微调创建了Llama-2-Chat的初始版本
  • 随后:使用基于人类反馈的强化学习(RLHF)方法来迭代地改进模型
    • 具体包括:
      • 拒绝采样(Rejection Sampling)
      • 近端策略优化(Proximal Policy Optimization, PPO)
    • RLHF阶段,人类偏好数据也在并行迭代,以保持奖励模型的更新

开源大模型和闭源大模型

  • 构建大模型不仅需要海量的数据,更依赖于强大的计算能力
  • 两类机构:
    • 一是选择将模型开源的组织
      • 代表:Meta AI、浪潮信息
    • 另一类则是保持模型闭源的公司
      • 通常伴随着专有技术和服务
      • 企业可以通过API等方式提供给客户使用
      • 不直接公开模型的细节或代码
      • 代表:OpenAI、百度等

源大模型开源体系

大模型时代挖掘模型能力的开发范式

1. Prompt工程

  • 精心构造提示(Prompt),直接调教大模型,解决实际问题
两种技术
  • 上下文学习(In-Context Learning, ICL):将任务说明及示例融入提示文本之中,利用模型自身的归纳能力,无需额外训练即可完成新任务的学习
  • 思维链提示(Chain-of-Thought, CoT):引入连贯的逻辑推理链条至提示信息内,显著增强了模型处理复杂问题时的解析深度与广度

2. Embedding辅助,给LLM外界大脑

大模型的若干局限性
  • 知识局限性:大模型的知识来源于训练数据,而这些数据主要来自于互联网上已经公开的资源,对于一些实时性的或者非公开的,由于大模型没有获取到相关数据,这部分知识也就无法被掌握。
  • 数据安全性:为了使得大模型能够具备相应的知识,就需要将数据纳入到训练集进行训练。然而,对于企业来说,数据的安全性至关重要,任何形式的数据泄露都可能对企业构成致命的威胁。
  • 大模型幻觉:由于大模型是基于概率统计进行构建的,其输出本质上是一系列数值运算。因此,有时会出现模型“一本正经地胡说八道”的情况,尤其是在大模型不具备的知识或不擅长的场景中。

3. 参数高效微调

  • 也被称为指令微调(Instruction Tuning)或者有监督微调(Supervised Fine-tuning, SFT)
  • 首先需要构建指令训练数据,然后通过有监督的方式对大模型的参数进行微调。经过模型微调后,大模型能够更好地遵循和执行人类指令,进而完成下游任务
  • 由于大模型的参数量巨大, 进行全量参数微调需要消耗非常多的算力 → 提出了参数高效微调(Parameter-efficient Fine-tuning) / 轻量化微调 (Lightweight Fine-tuning)
  • 方法通过训练极少的模型参数,同时保证微调后的模型表现可以与全量微调相媲美

大模型应用开发必知必会

客户端服务端
用户请求→gradio输入→大模型API
←回复streamlit←输出大模型本地部署

客户端

  • 客户端需要接受用户请求,并且能将回复返回给用户
  • 通常使用 GradioStreamlit 进行开发
Gradio
  • 输入输出组件、控制组件、布局组件几个基础模块
  • 输入输出组件用于展示内容和获取内容,如:Textbox文本、Image图像
  • 布局组件用于更好地规划组件的布局,如:Column(把组件放成一列)、Row(把组件放成一行)
    • 推荐使用gradio.Blocks()做更多丰富交互的界面,gradio.Interface()只支持单个函数交互
  • 控制组件用于直接调用函数,无法作为输入输出使用,如:Button(按钮)、ClearButton(清除按钮)

设计哲学:

将输入和输出组件与布局组件分开。

输入组件(如TextboxSlider等)用于接收用户输入

输出组件(如LabelImage等)用于显示函数的输出结果

布局组件(如TabsColumnsRow等)则用于组织和排列这些输入和输出组件,以创建结构化的用户界面

Streamlit
  • 官网: Streamlit 官方
  • Streamlit 中没有gradio的输入和输出概念,也没有布局组件的概念
  • Streamlit每个组件都是独立的,需要用什么直接查看官方文档
大致组件
  • 页面元素
    • 文本
    • 数据表格
    • 图标绘制(柱状图,散点图等等)
    • 输入(文本框,按钮,下拉框,滑块,复选框,文件上传,等等)
    • 多媒体(图片,音频,视频)
    • 布局和容器
    • Chat(聊天对话控件)
    • 状态(进度条,加载中,等等元素)
    • 第三方组件(提供了更加丰富的组件)
  • 应用逻辑
    • 导航和页面(可以切换页面)
    • 执行流程
    • 缓存和状态
    • 连接和加密(可连接数据库,也可以对内容进行加密处理)
    • 自定义组件
    • 公共组件(用户信息存储,帮助,以及输出html)
    • Config(使用配置文件,来定义一些内容)
  • 工具
    • 应用测试
    • 命令行

服务端

  • 服务端需要与大模型进行交互,大模型接受到用户请求后,经过复杂的计算,得到模型输出
服务端的两种方式
直接调用大模型API
  • 将请求直接发送给相应的服务商,如openai,讯飞星火等,等待API返回大模型回复
  • ✔️ 优点:
    1. 便捷性: 不需要关心模型的维护和更新,服务商通常会负责这些工作。
    2. 资源效率: 避免了本地硬件投资和维护成本,按需付费,灵活调整成本支出。
    3. 稳定性与安全性: 专业团队管理,可能提供更好的系统稳定性和数据安全性措施。
    4. 扩展性: API服务易于集成到现有的应用和服务中,支持高并发请求。
  • ✖️ 缺点:
    1. 网络延迟: 需要稳定的网络连接,可能会受到网络延迟的影响。
    2. 数据隐私: 数据需要传输到服务商的服务器,可能涉及数据安全和隐私问题。
    3. 成本控制: 高频次或大量数据的调用可能会导致较高的费用。
    4. 依赖性: 受制于服务商的政策变化,如价格调整、服务条款变更等。
大模型本地部署
  • 在本地GPU或者CPU上,下载模型文件,并基于推理框架进行部署大模型
  • ✔️ 优点:
    1. 数据主权: 数据完全在本地处理,对于敏感数据处理更为安全。
    2. 性能可控: 可以根据需求优化配置,减少网络延迟,提高响应速度。
    3. 成本固定: 初始投入后,长期运行成本相对固定,避免了按使用量付费的不确定性。
    4. 定制化: 更容易针对特定需求进行模型微调或扩展。
  • ✖️ 缺点:
    1. 硬件投资: 需要强大的计算资源,如高性能GPU,初期投资成本较高。
    2. 运维复杂: 需要自行管理模型的更新、维护和故障排查。
    3. 技术门槛: 对于非专业团队而言,模型的部署和优化可能较为复杂。
    4. 资源利用率: 在低负载情况下,本地硬件资源可能无法充分利用。
选哪种

选择哪种方式取决于具体的应用场景、数据敏感性、预算以及对延迟和性能的需求

baseline精读

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 导入所需的库
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
import streamlit as st

# 创建一个标题和一个副标题
st.title("💬 Yuan2.0 智能编程助手")

# 源大模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('IEITYuan/Yuan2-2B-Mars-hf', cache_dir='./')
# model_dir = snapshot_download('IEITYuan/Yuan2-2B-July-hf', cache_dir='./')

# 定义模型路径
path = './IEITYuan/Yuan2-2B-Mars-hf'
# path = './IEITYuan/Yuan2-2B-July-hf'

# 定义模型数据类型
torch_dtype = torch.bfloat16 # A10
# torch_dtype = torch.float16 # P100

# 定义一个函数,用于获取模型和tokenizer
@st.cache_resource
def get_model():
print("Creat tokenizer...")
tokenizer = AutoTokenizer.from_pretrained(path, add_eos_token=False, add_bos_token=False, eos_token='<eod>')
tokenizer.add_tokens(['<sep>', '<pad>', '<mask>', '<predict>', '<FIM_SUFFIX>', '<FIM_PREFIX>', '<FIM_MIDDLE>','<commit_before>','<commit_msg>','<commit_after>','<jupyter_start>','<jupyter_text>','<jupyter_code>','<jupyter_output>','<empty_output>'], special_tokens=True)

print("Creat model...")
model = AutoModelForCausalLM.from_pretrained(path, torch_dtype=torch_dtype, trust_remote_code=True).cuda()

print("Done.")
return tokenizer, model

# 加载model和tokenizer
tokenizer, model = get_model()

# 初次运行时,session_state中没有"messages",需要创建一个空列表
if "messages" not in st.session_state:
st.session_state["messages"] = []

# 每次对话时,都需要遍历session_state中的所有消息,并显示在聊天界面上
for msg in st.session_state.messages:
st.chat_message(msg["role"]).write(msg["content"])

# 如果用户在聊天输入框中输入了内容,则执行以下操作
if prompt := st.chat_input():
# 将用户的输入添加到session_state中的messages列表中
st.session_state.messages.append({"role": "user", "content": prompt})

# 在聊天界面上显示用户的输入
st.chat_message("user").write(prompt)

# 调用模型
prompt = "<n>".join(msg["content"] for msg in st.session_state.messages) + "<sep>" # 拼接对话历史
inputs = tokenizer(prompt, return_tensors="pt")["input_ids"].cuda()
outputs = model.generate(inputs, do_sample=False, max_length=1024) # 设置解码方式和最大生成长度
output = tokenizer.decode(outputs[0])
response = output.split("<sep>")[-1].replace("<eod>", '')

# 将模型的输出添加到session_state中的messages列表中
st.session_state.messages.append({"role": "assistant", "content": response})

# 在聊天界面上显示模型的输出
st.chat_message("assistant").write(response)

方案设计

概要
  • baseline基于源大模型的编程能力来解决用户的问题
  • 主要包含:一个Streamlit开发的客户端,一个部署好浪潮源大模型的服务端
  • 客户端接收到用户请求后,首先进行交互历史拼接,然后输入到服务端的浪潮源大模型,得到模型输出结果后,返回给客户端,用于回复用户的问题
详细设计
导入库↓导入所需要的依赖,包括 transformerstorchstreamlit。其中torch 魔搭本身已经安装,transformersstreamlit在第二步也安装完毕
模型下载↓Yuan2-2B-Mars支持通过多个平台进行下载,包括魔搭、HuggingFace、OpenXlab、百度网盘、WiseModel等。因为我们的机器就在魔搭,所以这里我们直接选择通过魔搭进行下载。模型在魔搭平台的地址为 IEITYuan/Yuan2-2B-Mars-hf。 模型下载使用的是 modelscope 中的 snapshot_download 函数,第一个参数为模型名称 IEITYuan/Yuan2-2B-Mars-hf,第二个参数 cache_dir 为模型保存路径,这里.表示当前路径。 模型大小约为4.1G,由于是从魔搭直接进行下载,速度会非常快。下载完成后,会在当前目录增加一个名为 IEITYuan 的文件夹,其中 Yuan2-2B-Mars-hf 里面保存着我们下载好的源大模型。
模型加载↓使用 transformers 中的 from_pretrained 函数来加载下载好的模型和tokenizer,并通过 .cuda() 将模型放置在GPU上。另外,这里额外使用了 streamlit 提供的一个装饰器 @st.cache_resource ,它可以用于缓存加载好的模型和tokenizer
读取用户输入↓使用 streamlit 提供的 chat_input() 来获取用户输入,同时将其保存到对话历史里,并通过st.chat_message("user").write(prompt) 在聊天界面上进行显示
对话历史拼接对于 Yuan2-2B-Mars 模型来说,输入需要在末尾添加 <sep>,模型输出到 <eod> 结束。如果输入是多轮对话历史,需要使用 <n> 进行拼接,并且在末尾添加 <sep>
模型调用输入的prompt需要先经tokenizer切分成token,并转成对应的id,并通过 .cuda() 将输入也放置在GPU上。然后调用 model.generate() 生成输出的id,并通过 tokenizer.decode() 将id转成对应的字符串。最后从字符串中提取模型生成的内容(即 <sep> 之后的字符串),并删除末尾的 <eod> ,得到最终的回复内容
显示模型输出得到回复内容后,将其保存到对话历史里,并通过st.chat_message("assistant").write(response) 在聊天界面上进行显示

尝试替换其他大模型

  • 浪潮信息源大模型上新:Yuan2-2B-July-hf,只需简单三步即可体验:
    • 双击打开baseline文件 AICamp_yuan_baseline/Task\ 1:零基础玩转源大模型/web_demo_2b.py
    • 将其中 # 源大模型下载# 定义模型路径 的地址修改为对应的模型即可
    • 在终端重新 启动Demo

代码替换

  • 替换前
1
2
3
4
5
6
# 源大模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('IEITYuan/Yuan2-2B-Mars-hf', cache_dir='./')

# 定义模型路径
path = './IEITYuan/Yuan2-2B-Mars-hf'
  • 替换模型后
1
2
3
4
5
6
# 源大模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('IEITYuan/Yuan2-2B-July-hf', cache_dir='./')

# 定义模型路径
path = './IEITYuan/Yuan2-2B-July-hf'
  • 在终端重新启动Demo

点击终端,然后输入如下命令,回车运行!

1
streamlit run AICamp_yuan_baseline/Task\ 1:零基础玩转源大模型/web_demo_2b.py --server.address 127.0.0.1 --server.port 6006
  • Copyrights © 2024-2025 brocademaple
  • 访问人数: | 浏览次数:

      请我喝杯咖啡吧~

      支付宝
      微信