目录
写在前面
-
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.0
,源2.0
和源2.0-M32
-
其中
源1.0
开放了模型API、高质量中文数据集和代码 -
源2.0
和源2.0-M32
采用全面开源策略,全系列模型参数和代码均可免费下载使用-
源2.0提出了局部注意力过滤增强机制(Localized Filtering-based Attention, LFA),它假设自然语言相邻词之间有更强的语义关联,因此针对局部依赖进行了建模,最后使得模型精度提高3.53%
-
源2.0-M32发布,它是一个混合专家(Mixture of Experts, MoE)大模型
-
包含32个专家,基于LFA+Attention Router的MoE模型结构
-
在数理逻辑、代码生成、知识等方面精度对标Llama3-70B,推理算力降至1/19
-
-
大模型时代挖掘模型能力的开发范式
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 | ←输出 | 大模型本地部署 | |
客户端
Gradio
-
有输入输出组件、控制组件、布局组件几个基础模块
-
输入输出组件用于展示内容和获取内容,如:
Textbox
文本、Image
图像 -
布局组件用于更好地规划组件的布局,如:
Column
(把组件放成一列)、Row
(把组件放成一行)-
推荐使用
gradio.Blocks()
做更多丰富交互的界面,gradio.Interface()
只支持单个函数交互
-
-
控制组件用于直接调用函数,无法作为输入输出使用,如:
Button
(按钮)、ClearButton
(清除按钮)
-
设计哲学:
将输入和输出组件与布局组件分开。
输入组件(如
Textbox
、Slider
等)用于接收用户输入输出组件(如
Label
、Image
等)用于显示函数的输出结果布局组件(如
Tabs
、Columns
、Row
等)则用于组织和排列这些输入和输出组件,以创建结构化的用户界面
Streamlit
大致组件
-
页面元素
-
文本
-
数据表格
-
图标绘制(柱状图,散点图等等)
-
输入(文本框,按钮,下拉框,滑块,复选框,文件上传,等等)
-
多媒体(图片,音频,视频)
-
布局和容器
-
Chat(聊天对话控件)
-
状态(进度条,加载中,等等元素)
-
第三方组件(提供了更加丰富的组件)
-
-
应用逻辑
-
导航和页面(可以切换页面)
-
执行流程
-
缓存和状态
-
连接和加密(可连接数据库,也可以对内容进行加密处理)
-
自定义组件
-
公共组件(用户信息存储,帮助,以及输出html)
-
Config(使用配置文件,来定义一些内容)
-
-
工具
-
应用测试
-
命令行
-
服务端
-
服务端需要与大模型进行交互,大模型接受到用户请求后,经过复杂的计算,得到模型输出
服务端的两种方式
直接调用大模型API
-
将请求直接发送给相应的服务商,如openai,讯飞星火等,等待API返回大模型回复
-
✔️ 优点:
-
便捷性: 不需要关心模型的维护和更新,服务商通常会负责这些工作。
-
资源效率: 避免了本地硬件投资和维护成本,按需付费,灵活调整成本支出。
-
稳定性与安全性: 专业团队管理,可能提供更好的系统稳定性和数据安全性措施。
-
扩展性: API服务易于集成到现有的应用和服务中,支持高并发请求。
-
-
✖️ 缺点:
-
网络延迟: 需要稳定的网络连接,可能会受到网络延迟的影响。
-
数据隐私: 数据需要传输到服务商的服务器,可能涉及数据安全和隐私问题。
-
成本控制: 高频次或大量数据的调用可能会导致较高的费用。
-
依赖性: 受制于服务商的政策变化,如价格调整、服务条款变更等。
-
大模型本地部署
-
在本地GPU或者CPU上,下载模型文件,并基于推理框架进行部署大模型
-
✔️ 优点:
-
数据主权: 数据完全在本地处理,对于敏感数据处理更为安全。
-
性能可控: 可以根据需求优化配置,减少网络延迟,提高响应速度。
-
成本固定: 初始投入后,长期运行成本相对固定,避免了按使用量付费的不确定性。
-
定制化: 更容易针对特定需求进行模型微调或扩展。
-
-
✖️ 缺点:
-
硬件投资: 需要强大的计算资源,如高性能GPU,初期投资成本较高。
-
运维复杂: 需要自行管理模型的更新、维护和故障排查。
-
技术门槛: 对于非专业团队而言,模型的部署和优化可能较为复杂。
-
资源利用率: 在低负载情况下,本地硬件资源可能无法充分利用。
-
选哪种
选择哪种方式取决于具体的应用场景、数据敏感性、预算以及对延迟和性能的需求
baseline精读
完整代码
# 导入所需的库 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开发的客户端,一个部署好浪潮源大模型的服务端
-
客户端接收到用户请求后,首先进行交互历史拼接,然后输入到服务端的浪潮源大模型,得到模型输出结果后,返回给客户端,用于回复用户的问题
详细设计
导入库↓ | 导入所需要的依赖,包括 TRANSFORMERS ,TORCH 和 STREAMLIT 。其中TORCH 魔搭本身已经安装,TRANSFORMERS 和 STREAMLIT 在第二步也安装完毕 |
---|---|
模型下载↓ | 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
-
代码替换
-
替换前
# 源大模型下载 from modelscope import snapshot_download model_dir = snapshot_download('IEITYuan/Yuan2-2B-Mars-hf', cache_dir='./') # 定义模型路径 path = './IEITYuan/Yuan2-2B-Mars-hf'
-
替换模型后
# 源大模型下载 from modelscope import snapshot_download model_dir = snapshot_download('IEITYuan/Yuan2-2B-July-hf', cache_dir='./') # 定义模型路径 path = './IEITYuan/Yuan2-2B-July-hf'
-
在终端重新启动Demo
点击终端,然后输入如下命令,回车运行!
streamlit run AICamp_yuan_baseline/Task\ 1:零基础玩转源大模型/web_demo_2b.py --server.address 127.0.0.1 --server.port 6006