-
Datawhale 夏令营第四期 大模型应用技术开发 笔记1
-
baseline搭建
-
1、开通阿里云PAI-DSW试用
-
2、在魔搭社区创建PAI实例,并启动
-
3、关闭实例
-
-
背景知识
-
一、什么是大模型
-
定义
-
为了对人类语言的内在规律进行建模,研究者们提出使用语言模型(language model)来准确预测词序列中 下一个词 或者 缺失的词 的概率。
-
-
分类
-
统计语言模型(Statistical Language Model, SLM):使用马尔可夫假设(Markov Assumption)来建模语言序列的 𝑛 元(𝑛-gram)语言模型
-
神经语言模型(Neural Language Model, NLM):基于神经网络构建语言模型,如循环神经网络(Recurrent Neural Networks, RNN),并学习上下文相关的词表示(即分布式词向量),也被称为词嵌入(Word Embedding)。代表性工作:word2vec
-
预训练语言模型(Pre-trained Language Model, PLM):使用大量的无标注数据预训练双向 LSTM(Bidirectional LSTM, biLSTM)或者Transformer,然后在下游任务上进行微调(Fine-Tuning)。代表性工作:ELMo、BERT、GPT-1/2
-
大语言模型(Large Language Model, LLM):基于“扩展法则”(Scaling Law),即通过增加模型参数或训练数据,可以提升下游任务的性能,同时具有小模型不具有的“涌现能力”(Emergent Abilities)。代表性工作:GPT-3、ChatGPT、Claude、Llama
-
-
-
二、大模型是怎么构建的
-
大模型的构建过程可以分为预训练(Pretraining)、有监督微调(Supervised Fine-tuning, SFT)、基于人类反馈的强化学习对齐(Reinforcement Learning from Human Feedback, RLHF)三个阶段
-
1. 预训练
-
预训练指使用海量的数据进行模型参数的初始学习,旨在为模型参数寻找到一个优质的“起点”。
-
创新思路——通过大规模文本数据预训练,打造能够应对广泛任务的通用解决方案,并在GPT-3中将这一理念扩展至前所未有的超大规模
-
“解码器架构+预测下一个词”的策略证明了其卓越效能,成为了当前主流的大模型技术路线
-
在预训练过程中,首要任务是搜集和清洗海量的文本数据,确保剔除潜在的有害内容。
-
多数开源模型的预训练均基于数T的token。
-
预训练过程中还涉及诸多细节,诸如数据配比、学习率调度、模型行为监测等,这些往往缺乏公开的最佳实践指导,需要研发团队具备深厚的训练经验与故障排查能力,以规避训练过程中的回溯与重复迭代,节约计算资源,提高训练效率。
-
-
2. 有监督微调
-
经过大规模预训练后,模型已经具备较强的模型能力,能够编码丰富的世界知识,但是由于预训练任务形式所限,这些模型更擅长于文本补全,并不适合直接解决具体的任务。
-
模型本身在下游任务解决上的能力仍受限。为了克服这一局限,预训练后的大型语言模型往往需经历微调过程,以提升其在特定任务中的表现。
-
比较广泛使用的微调技术是“有监督微调”(也叫做指令微调,Instruction Tuning),该方法利用成对的任务输入与预期输出数据,训练模型学会以问答的形式解答问题,从而解锁其任务解决潜能。经过指令微调后,大语言模型能够展现出较强的指令遵循能力,可以通过零样本学习的方式解决多种下游任务。
-
相较于预训练所需的海量数据,指令微调所需数据量显著减少,从几十万到上百万条不等的数据,均可有效激发模型的通用任务解决能力
-
-
3. 基于人类反馈的强化学习对齐
-
大语言模型与人类期望、需求及价值观的对齐(Alignment)至关重要,这对于大模型的应用具有重要的意义。
-
OpenAI在 2022 年初发布了 InstructGPT 论文,详尽阐述了如何实现语言模型与人类对齐。论文提出了基于人类反馈的强化学习对齐(Reinforcement Learning from Human Feedback, RLHF),通过指令微调后的强化学习,提升模型的人类对齐度。RLHF的核心在于构建一个反映人类价值观的奖励模型(Reward Model)。这一模型的训练依赖于专家对模型多种输出的偏好排序,通过偏好数据训练出的奖励模型能够有效评判模型输出的质量。
-
其他使用 SFT 方式来达到与 RLHF 相似的效果,直接偏好优化(Direct Preference Optimization, DPO),它通过与有监督微调相似的复杂度实现模型与人类对齐,是一种典型的不需要强化学习的对齐算法
-
-
4、Llama-2-Chat训练过程
-
整个过程起始于利用公开数据进行预训练,获得Llama-2。在此之后,通过有监督微调创建了Llama-2-Chat的初始版本。随后,使用基于人类反馈的强化学习(RLHF)方法来迭代地改进模型,具体包括拒绝采样(Rejection Sampling)和近端策略优化(Proximal Policy Optimization, PPO)。在RLHF阶段,人类偏好数据也在并行迭代,以保持奖励模型的更新。
-
-
-
三、开源大模型和闭源大模型
-
一是选择将模型开源的组织
-
另一类则是保持模型闭源的公司
-
-
五、大模型时代挖掘模型能力的开发范式
-
1. 有手就行的Prompt工程
-
Prompt工程(Prompt Engineering)是指通过精心构造提示(Prompt),直接调教大模型,解决实际问题。为了更充分地挖掘大模型的潜能,出现了以下两种技术:
-
- 上下文学习(In-Context Learning, ICL):将任务说明及示例融入提示文本之中,利用模型自身的归纳能力,无需额外训练即可完成新任务的学习。
-
- 思维链提示(Chain-of-Thought, CoT):引入连贯的逻辑推理链条至提示信息内,显著增强了模型处理复杂问题时的解析深度与广度。
-
-
-
2. Embedding辅助给LLM外接大脑
-
大模型无法满足我们需求的情况,主要有以下几方面原因
-
- 知识局限性:大模型的知识来源于训练数据,而这些数据主要来自于互联网上已经公开的资源,对于一些实时性的或者非公开的,由于大模型没有获取到相关数据,这部分知识也就无法被掌握。
-
- 数据安全性:为了使得大模型能够具备相应的知识,就需要将数据纳入到训练集进行训练。然而,对于企业来说,数据的安全性至关重要,任何形式的数据泄露都可能对企业构成致命的威胁。
-
- 大模型幻觉:由于大模型是基于概率统计进行构建的,其输出本质上是一系列数值运算。因此,有时会出现模型“一本正经地胡说八道”的情况,尤其是在大模型不具备的知识或不擅长的场景中。
-
-
将知识提前转成Embedding向量,存入知识库,然后通过检索将知识作为背景信息,这样就相当于给LLM外接大脑,使大模型能够运用这些外部知识,生成准确且符合上下文的答案,同时能够减少模型幻觉的出现。
-
-
3. 参数高效微调
-
问题
-
- 大模型在当前任务上能力不佳,如果提升其能力?
-
- 另外,怎么使大模型学习其本身不具备的能力呢?
-
-
答案
-
模型微调
-
模型微调也被称为指令微调(Instruction Tuning)或者有监督微调(Supervised Fine-tuning, SFT),首先需要构建指令训练数据,然后通过有监督的方式对大模型的参数进行微调。经过模型微调后,大模型能够更好地遵循和执行人类指令,进而完成下游任务。
-
由于大模型的参数量巨大, 进行全量参数微调需要消耗非常多的算力。为了解决这一问题,研究者提出了参数高效微调(Parameter-efficient Fine-tuning),也称为轻量化微调 (Lightweight Fine-tuning),这些方法通过训练极少的模型参数,同时保证微调后的模型表现可以与全量微调相媲美。
-
-
-
-
六、大模型应用开发 必知必会
-
通常,一个完整的大模型应用包含一个客户端和一个服务端。客户端接收到用户请求后,将请求输入到服务端,服务端经过计算得到输出后,返回给客户端回复用户的请求。
-
两种
-
1. 客户端
-
在大模型应用中,客户端需要接受用户请求,并且能将回复返回给用户。
-
客户端通常使用 Gradio 和 Streamlit 进行开发。
-
Gradio 基本概念
-
有输入输出组件、控制组件、布局组件几个基础模块
-
- 输入输出组件用于展示内容和获取内容,如:Textbox文本、Image图像
-
- 布局组件用于更好地规划组件的布局,如:Column(把组件放成一列)、Row(把组件放成一行)。 推荐使用gradio.Blocks()做更多丰富交互的界面,gradio.Interface()只支持单个函数交互
-
- 控制组件用于直接调用函数,无法作为输入输出使用,如:Button(按钮)、ClearButton(清除按钮)
-
Gradio的设计哲学是将输入和输出组件与布局组件分开。输入组件(如Textbox、Slider等)用于接收用户输入,输出组件(如Label、Image等)用于显示函数的输出结果。而布局组件(如Tabs、Columns、Row等)则用于组织和排列这些输入和输出组件,以创建结构化的用户界面。
-
-
Streamlit 基础概念
-
- Streamlit每个组件都是独立的,需要用什么直接查看官方文档即可,大致有如下几种组件:
-
- 页面元素
-
- 文本
-
- 数据表格
-
- 图标绘制(柱状图,散点图等等)
-
- 输入(文本框,按钮,下拉框,滑块,复选框,文件上传,等等)
-
- 多媒体(图片,音频,视频)
-
- 布局和容器
-
- Chat(聊天对话控件)
-
- 状态(进度条,加载中,等等元素)
-
- 第三方组件(提供了更加丰富的组件)
-
-
- 应用逻辑
-
- 导航和页面(可以切换页面)
-
- 执行流程
-
- 缓存和状态
-
- 连接和加密(可连接数据库,也可以对内容进行加密处理)
-
- 自定义组件
-
- 公共组件(用户信息存储,帮助,以及输出html)
-
- Config(使用配置文件,来定义一些内容)
-
-
- 工具
-
- 应用测试
-
- 命令行
-
-
-
-
服务端
-
在大模型应用中,服务端需要与大模型进行交互,大模型接受到用户请求后,经过复杂的计算,得到模型输出。
-
服务端主要有以下两种方式
-
- 直接调用大模型API:将请求直接发送给相应的服务商,如openai,讯飞星火等,等待API返回大模型回复
-
- 大模型本地部署:在本地GPU或者CPU上,下载模型文件,并基于推理框架进行部署大模型
-
-
-
-
-
七、完整baseline代码
-
1. 代码
-
# 导入所需的库 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)
-
2. baseline方案设计
-
2.1 概要设计
-
baseline基于源大模型的编程能力来解决用户的问题,主要包含一个Streamlit开发的客户端,以及一个部署好浪潮源大模型的服务端。客户端接收到用户请求后,首先进行交互历史拼接,然后输入到服务端的浪潮源大模型,得到模型输出结果后,返回给客户端,用于回复用户的问题。
-
-
2.2 详细设计
-
- 1. 导入库:
-
导入所需要的依赖,包括 transformers,torch 和 streamlit。其中torch 魔搭本身已经安装,transformers 和 streamlit在第二步也安装完毕。
-
-
2. 模型下载:
-
3. 模型加载:
-
使用 transformers 中的 from_pretrained 函数来加载下载好的模型和tokenizer,并通过 .cuda() 将模型放置在GPU上。另外,这里额外使用了 streamlit 提供的一个装饰器 @st.cache_resource ,它可以用于缓存加载好的模型和tokenizer。
-
-
4. 读取用户输入:
-
使用 streamlit 提供的 chat_input() 来获取用户输入,同时将其保存到对话历史里,并通过st.chat_message("user").write(prompt) 在聊天界面上进行显示。
-
-
5. 对话历史拼接:
-
对于 Yuan2-2B-Mars 模型来说,输入需要在末尾添加 <sep>,模型输出到 <eod> 结束。如果输入是多轮对话历史,需要使用 <n> 进行拼接,并且在末尾添加 <sep>。
-
-
6. 模型调用:
-
输入的prompt需要先经tokenizer切分成token,并转成对应的id,并通过 .cuda() 将输入也放置在GPU上。然后调用 model.generate() 生成输出的id,并通过 tokenizer.decode() 将id转成对应的字符串。最后从字符串中提取模型生成的内容(即 <sep> 之后的字符串),并删除末尾的 <eod> ,得到最终的回复内容。
-
-
7. 显示模型输出:
-
得到回复内容后,将其保存到对话历史里,并通过st.chat_message("assistant").write(response) 在聊天界面上进行显示。
-
- 1. 导入库:
-
-
八、应用实践
-
-
-
Datawhale 夏令营第四期 大模型应用技术开发 笔记1
最新推荐文章于 2024-10-16 18:47:39 发布