Datewhale AI夏令营

搭建第一个编程助手

背景知识

1. 什么是大模型

大模型是指具有大规模参数和复杂计算结构的机器学习模型。这些模型通常由深度神经网络构建而成,拥有数十亿甚至数千亿个参数。大模型的设计目的是为了提高模型的表达能力和预测性能,能够处理更加复杂的任务和数据。大模型在各种领域都有广泛的应用,包括自然语言处理、计算机视觉、语音识别和推荐系统等。大模型通过训练海量数据来学习复杂的模式和特征,具有更强大的泛化能力,可以对未见过的数据做出准确的预测。为了对人类语言的内在规律进行建模,研究者们提出使用语言模型(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。
    在这里插入图片描述
    大模型优势

  • 巨大的规模: 大模型包含数十亿个参数,模型大小可以达到数百 GB 甚至更大。巨大的模型规模使大模型具有强大的表达能力和学习能力。

  • 涌现能力:涌现(英语:emergence)或称创发、突现、呈展、演生,是一种现象,为许多小实体相互作用后产生了大实体,而这个大实体展现了组成它的小实体所不具有的特性。引申到模型层面,涌现能力指的是当模型的训练数据突破一定规模,模型突然涌现出之前小模型所没有的、意料之外的、能够综合分析和解决更深层次问题的复杂能力和特性,展现出类似人类的思维和智能。涌现能力也是大模型最显著的特点之一。

  • 更好的性能和泛化能力: 大模型通常具有更强大的学习能力和泛化能力,能够在各种任务上表现出色,包括自然语言处理、图像识别、语音识别等。

  • 多任务学习: 大模型通常会一起学习多种不同的 NLP 任务,如机器翻译、文本摘要、问答系统等。这可以使模型学习到更广泛和泛化的语言理解能力。

  • 大数据训练: 大模型需要海量的数据来训练,通常在 TB 以上甚至 PB 级别的数据集。只有大量的数据才能发挥大模型的参数规模优势。

  • 强大的计算资源: 训练大模型通常需要数百甚至上千个 GPU,以及大量的时间,通常在几周到几个月。

  • 迁移学习和预训练: 大模型可以通过在大规模数据上进行预训练,然后在特定任务上进行微调,从而提高模型在新任务上的性能。

  • 自监督学习: 大模型可以通过自监督学习在大规模未标记数据上进行训练,从而减少对标记数据的依赖,提高模型的效能。

  • 领域知识融合: 大模型可以从多个领域的数据中学习知识,并在不同领域中进行应用,促进跨领域的创新。

  • 自动化和效率:大模型可以自动化许多复杂的任务,提高工作效率,如自动编程、自动翻译、自动摘要等。

2. 大模型是怎么构建的

通常来说,大模型的构建过程可以分为预训练(Pretraining)、有监督微调(Supervised Fine-tuning, SFT)、基于人类反馈的强化学习对齐(Reinforcement Learning from Human Feedback, RLHF)三个阶段。

预训练:

预训练指使用海量的数据进行模型参数的初始学习,旨在为模型参数寻找到一个优质的“起点”。这一概念最初在计算机视觉领域萌芽,通过在ImageNet(一个大型图像数据集)上的训练,为模型参数奠定了坚实的基础。随后,这一理念被自然语言处理(NLP)领域采纳,word2vec开创先河,利用未标注文本构建词嵌入模型;ELMo、BERT及GPT-1则进一步推动了“预训练-微调”范式的普及。

起初,预训练技术专注于解决特定类别的下游任务,例如文本分类、序列标注、序列到序列生成等传统NLP任务。OpenAI在GPT-2的研究中,提出了一种创新思路——通过大规模文本数据预训练,打造能够应对广泛任务的通用解决方案,并在GPT-3中将这一理念扩展至前所未有的超大规模。

在BERT等早期预训练模型中,模型架构和训练任务呈现出多样化特征。然而,随着GPT系列模型的兴起,“解码器架构+预测下一个词”的策略证明了其卓越效能,成为了当前主流的大模型技术路线。

在预训练过程中,首要任务是搜集和清洗海量的文本数据,确保剔除潜在的有害内容。鉴于模型的知识库几乎完全源自训练数据,数据的质量与多样性对模型性能至关重要。因此,获取高质、多元的数据集,并对其进行严谨的预处理,是打造高性能语言模型的关键步骤。

有监督微调:

经过大规模预训练后,模型已经具备较强的模型能力,能够编码丰富的世界知识,但是由于预训练任务形式所限,这些模型更擅长于文本补全,并不适合直接解决具体的任务。

尽管引入了诸如上下文学习(In-Context Learning, ICL)等提示学习策略以增强模型的适应性,但模型本身在下游任务解决上的能力仍受限。为了克服这一局限,预训练后的大型语言模型往往需经历微调过程,以提升其在特定任务中的表现

目前来说,比较广泛使用的微调技术是“有监督微调”(也叫做指令微调,Instruction Tuning),该方法利用成对的任务输入与预期输出数据,训练模型学会以问答的形式解答问题,从而解锁其任务解决潜能。经过指令微调后,大语言模型能够展现出较强的指令遵循能力,可以通过零样本学习的方式解决多种下游任务。

然而,值得注意的是,指令微调并非无中生有地传授新知,而是更多地扮演着催化剂的角色,激活模型内在的潜在能力,而非单纯地灌输信息

基于人类反馈的强化学习对齐:

除了提升任务的解决能力外,大语言模型与人类期望、需求及价值观的对齐(Alignment)至关重要,这对于大模型的应用具有重要的意义

OpenAI在 2022 年初发布了 InstructGPT 论文,详尽阐述了如何实现语言模型与人类对齐。论文提出了基于人类反馈的强化学习对齐(Reinforcement Learning from Human Feedback, RLHF),通过指令微调后的强化学习,提升模型的人类对齐度。RLHF的核心在于构建一个反映人类价值观的奖励模型(Reward Model)。这一模型的训练依赖于专家对模型多种输出的偏好排序,通过偏好数据训练出的奖励模型能够有效评判模型输出的质量。

3. 开源大模型和闭源大模型

  • 一是选择将模型开源的组织,他们秉持着促进学术交流和技术创新的理念,让全球的研究者和开发者都能受益于这些模型。通过开放模型的代码和数据集,他们加速了整个AI社区的发展,促进了创新和技术的民主化。这一阵营的代表有Meta AI、浪潮信息等。
  • 另一类则是保持模型闭源的公司,它们通常将模型作为核心竞争力,用于提供专有服务或产品,以维持商业优势。闭源模型通常伴随着专有技术和服务,企业可以通过API等方式提供给客户使用,而不直接公开模型的细节或代码。这种模式有助于保障公司的商业利益,同时也为用户提供了稳定和安全的服务。这一阵营的代表有OpenAI、百度等。

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

Prompt工程

Prompt工程(Prompt Engineering)是指通过精心构造提示(Prompt),直接调教大模型,解决实际问题。
为了更充分地挖掘大模型的潜能,出现了以下两种技术:

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

Embedding辅助给LLM外接大脑

尽管大模型具有非常出色的能力,然而在实际应用场景中,仍然会出现大模型无法满足我们需求的情况,主要有以下几方面原因:

  • 知识局限性:大模型的知识来源于训练数据,而这些数据主要来自于互联网上已经公开的资源,对于一些实时性的或者非公开的,由于大模型没有获取到相关数据,这部分知识也就无法被掌握。
  • 数据安全性:为了使得大模型能够具备相应的知识,就需要将数据纳入到训练集进行训练。然而,对于企业来说,数据的安全性至关重要,任何形式的数据泄露都可能对企业构成致命的威胁。
  • 大模型幻觉:由于大模型是基于概率统计进行构建的,其输出本质上是一系列数值运算。因此,有时会出现模型“一本正经地胡说八道”的情况,尤其是在大模型不具备的知识或不擅长的场景中。
    因此,将知识提前转成Embedding向量,存入知识库,然后通过检索将知识作为背景信息,这样就相当于给LLM外接大脑,使大模型能够运用这些外部知识,生成准确且符合上下文的答案,同时能够减少模型幻觉的出现。

参数高效微调

在实际应用场景中,大模型还会经常出现以下问题:

  • 大模型在当前任务上能力不佳,如果提升其能力?
  • 另外,怎么使大模型学习其本身不具备的能力呢?
    这些问题的答案是模型微调。
    模型微调也被称为指令微调(Instruction Tuning)或者有监督微调(Supervised Fine-tuning, SFT),首先需要构建指令训练数据,然后通过有监督的方式对大模型的参数进行微调。经过模型微调后,大模型能够更好地遵循和执行人类指令,进而完成下游任务。
    然而,由于大模型的参数量巨大, 进行全量参数微调需要消耗非常多的算力。为了解决这一问题,研究者提出了参数高效微调(Parameter-efficient Fine-tuning),也称为轻量化微调 (Lightweight Fine-tuning),这些方法通过训练极少的模型参数,同时保证微调后的模型表现可以与全量微调相媲美。

5. 大模型应用开发

开发流程如下:
在这里插入图片描述

客户端:

Gradio:
Gradio 有输入输出组件、控制组件、布局组件几个基础模块,其中

  • 输入输出组件用于展示内容和获取内容,如:Textbox文本、Image图像
  • 布局组件用于更好地规划组件的布局,如:Column(把组件放成一列)、Row(把组件放成一行)
    • 推荐使用gradio.Blocks()做更多丰富交互的界面,gradio.Interface()只支持单个函数交互
  • 控制组件用于直接调用函数,无法作为输入输出使用,如:Button(按钮)、ClearButton(清除按钮)
    Gradio的设计哲学是将输入和输出组件与布局组件分开。输入组件(如Textbox、Slider等)用于接收用户输入,输出组件(如Label、Image等)用于显示函数的输出结果。而布局组件(如Tabs、Columns、Row等)则用于组织和排列这些输入和输出组件,以创建结构化的用户界面。

Streamlit: 中没有gradio的输入和输出概念,也没有布局组件的概念。

服务端:

  • 直接调用大模型API:将请求直接发送给相应的服务商,如openai,讯飞星火等,等待API返回大模型回复
  • 大模型本地部署:在本地GPU或者CPU上,下载模型文件,并基于推理框架进行部署大模型

baseline实践

1. 完整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='./')

# 定义模型路径
path = './IEITYuan/Yuan2-2B-Mars-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 概要设计

![在这里插入图片描述]

2.2 详细设计

![在这里插入图片描述]

  1. 导入库:
    导入所需要的依赖,包括 transformers,torch 和 streamlit。其中torch 魔搭本身已经安装,transformers 和 streamlit在第二步也安装完毕。
  2. 模型下载:
    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 里面保存着我们下载好的源大模型。
  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 模型来说,输入需要在末尾添加 ,模型输出到 结束。如果输入是多轮对话历史,需要使用 进行拼接,并且在末尾添加 。
  6. 模型调用:
    输入的prompt需要先经tokenizer切分成token,并转成对应的id,并通过 .cuda() 将输入也放置在GPU上。然后调用 model.generate() 生成输出的id,并通过 tokenizer.decode() 将id转成对应的字符串。最后从字符串中提取模型生成的内容(即 之后的字符串),并删除末尾的 ,得到最终的回复内容。
  7. 显示模型输出:
    得到回复内容后,将其保存到对话历史里,并通过st.chat_message(“assistant”).write(response) 在聊天界面上进行显示。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值