动手点关注
干货不迷路
本文旨在让无大模型开发背景的工程师或者技术爱好者无痛理解大语言模型应用开发的理论和主流工具,因此会先从与LLM应用开发相关的基础概念谈起,并不刻意追求极致的严谨和完备,而是从直觉和本质入手,结合笔者调研整理及消化理解,帮助大家能够更容易的理解LLM技术全貌,大家可以基于本文衍生展开,结合自己感兴趣的领域深入研究。若有不准确或者错误的地方也希望大家能够留言指正。
本文体系完整,内容丰富,分多次连载 。
第一部分 基础概念
1.机器学习场景类别
2.机器学习类型(LLM相关)
3.深度学习的兴起
4.基础模型
第二部分 应用挑战
1.问题定义与基本思路
2.基本流程与相关技术
1)Tokenization与Embbeding
2)向量数据库
3)finetune(微调)
- 背景挑战
- 方案理论
- 工具实践
4)模型部署与推理
5)prompt
6)编排与集成
7)预训练
第三部分 场景案例
常用参考
2.基本流程与相关技术
2) finetune(微调)-工具实践篇
前面大篇幅介绍了微调相关的理论知识,接下来介绍四款工具或产品,借助它们我们可以比较方便的完成参数高效的finetune。为了让大家能够体验下面的例子,普及了两篇关于colab使用的文章(点公众号查看),不熟悉的朋友可以查看:
它是由huggingface开发的一款开源参数高效微调(Parameter-Efficient Fine-Tuning (PEFT) )库。它无 缝和🤗 Accelerate集成 ,可用于利用了 DeepSpeed 和 Big Model Inference 的大规模模型。它支持很多刚才提到的方法实现,如下:
- LoRA: LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS
- Prefix Tuning: Prefix-Tuning: Optimizing Continuous Prompts for Generation, P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks
- P-Tuning: GPT Understands, Too
- Prompt Tuning: The Power of Scale for Parameter-Efficient Prompt Tuning
- AdaLoRA: Adaptive Budget Allocation for Parameter-Efficient Fine-Tuning
简单以lora为例:
from transformers import AutoModelForSeq2SeqLM
from peft import get_peft_config, get_peft_model, LoraConfig, TaskType
model_name_or_path = "bigscience/mt0-large"
tokenizer_name_or_path = "bigscience/mt0-large"
peft_config = LoraConfig(
task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1
)
model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path)
#get_peft_model会初始化PeftModel把原模型作为base模型,并在各个self-attention层加入lora层,同时改写模型forward的计算方式。
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
# output: trainable params: 2359296 || all params: 1231940608 || trainable%: 0.19151053100118282
可以看到参数量仅占总参数量的0.19%。
因为peftModel重写了原始model的save_pretrained函数,只把lora层的权重进行存储,因此model.save_pretrained只会存储lora权重。所以,当得到 lora model后,需要和原来的basemodel合并,然后才能进行推理,例如( alpaca-lora中权重合并的):
import os
import torch
import transformers
from peft import PeftModel
from transformers import LlamaForCausalLM, LlamaTokenizer # noqa: F402
BASE_MODEL = os.environ.get("BASE_MODEL", None)
assert (
BASE_MODEL
), "Please specify a value for BASE_MODEL environment variable, e.g. `export BASE_MODEL=huggyllama/llama-7b`" # noqa: E501
tokenizer = LlamaTokenizer.from_pretrained(BASE_MODEL)
base_model = LlamaForCausalLM.from_pretrained(
BASE_MODEL,
load_in_8bit=False,
torch_dtype=torch.float16,
device_map={"": "cpu"},
)
first_weight = base_model.model.layers[0].self_attn.q_proj.weight
first_weight_old = first_weight.clone()
lora_model = PeftModel.from_pretrained(
base_model,
"tloen/alpaca-lora-7b",
device_map={"": "cpu"},
torch_dtype=torch.float16,
)
lora_weight = lora_model.base_model.model.model.layers[
0
].self_attn.q_proj.weight
assert torch.allclose(first_weight_old, first_weight)
# merge weights - new merging method from peft
lora_model = lora_model.merge_and_unload()
lora_model.train(False)
# did we do anything?
assert not torch.allclose(first_weight_old, first_weight)
lora_model_sd = lora_model.state_dict()
deloreanized_sd = {
k.replace("base_model.model.", ""): v
for k, v in lora_model_sd.items()
if "lora" not in k
}
LlamaForCausalLM.save_pretrained(
base_model, "./hf_ckpt", state_dict=deloreanized_sd, max_shard_size="400MB"
)
在硬件(单卡A100 80GB GPU, CPU RAM 64GB)下的 表现对比:
Model | |||
Full Finetuning | |||
PEFT-LoRA PyTorch | |||
PEFT-LoRA DeepSpeed with CPU Offloading | |||
bigscience/T0_3B (3B params) | |||
47.14GB GPU / 2.96GB CPU | |||
14.4GB GPU / 2.96GB CPU | |||
9.8GB GPU / 17.8GB CPU | |||
bigscience/mt0-xxl (12B params) | |||
OOM GPU | |||
56GB GPU / 3GB CPU | |||
22GB GPU / 52GB CPU | |||
bigscience/bloomz-7b1(7B params) | |||
OOM GPU | |||
32GB GPU / 3.8GB CPU | |||
18.1GB GPU / 35GB CPU | |||
此外,与基础的bigscience/T0_3B模型的11GB大小相比,lora模型大小仅为19MB。
Submission Name | |
Accuracy | |
Humanbaseline (crowdsourced) | |
0.897 | |
Flan-T5 | |
0.892 | |
lora-t0-3b | |
0.863 | |
因此,这是得使用16GB和24GB GPU等消费级硬件,通过PEFT方法也可以实现与SoTA相媲美的性能。
点此实验:https://colab.research.google.com/drive/1jCkpikz0J2o20FBQmYmAGdiKmJGOMo-o?usp=sharing
- adapter-transformers
adapter-transformers 也是 HuggingFace 的基于 Transformers 库的一个扩展库。它允许用户在模型中添加适配器,并在不同的语言模型之间共享和重用适配器,并且搭建了AdapterHub使得大家能够更方便的获取或者分享这些adpater。并且这些adapter模块化设计,足够轻量和独立,可扩展,与pretrain大模型分离,可按需装配,且采用了参数高效微调的技术,减少微调的成本。
from transformers import RobertaConfig, RobertaModelWithHeads
config = RobertaConfig.from_pretrained(
"roberta-base",
num_labels=2,
)
model = RobertaModelWithHeads.from_pretrained(
"roberta-base",
config=config,
)
# Add a new adapter
model.add_adapter("rotten_tomatoes")
# Add a matching classification head
model.add_classification_head(
"rotten_tomatoes",
num_labels=2,
id2label={ 0: "👎", 1: "👍"}
)
# Activate the adapter
model.train_adapter("rotten_tomatoes")
trainer = AdapterTrainer(
model=model,
args=training_args,
train_dataset=dataset["train"],
eval_dataset=dataset["validation"],
compute_metrics=compute_accuracy,
)
trainer.train()
- OpenDelta
清华刘知远团队出品的关于参数高效优化的开源工具包,包含常见的deltatuning方法,如prefix-tuning, adapters, Lora等。还包含一些特色功能,如可视化的查看模型结构及微调模块。
点此体验:https://colab.research.google.com/drive/1uAhgAdc8Qr42UKYDlgUv0f7W1-gAFwGo?usp=sharing
产品级微调体验-OpenAI FineTune
对于基于OpenAI的大模型开发者,如何finetune自己的大模型,OpenAI提供比较详尽的文档,下面结合其文档,一起来体验,这个过程不仅能够更直观的了解finetune的过程,也能够给开发者一个参考,如何设计一个对开发者友好的大模型开发者产品。
微调的特点就是可以在大量标记的数据集上训练,这比prompt中提供的少量例子的方式( few shot) 能 够更好的 提升模型效果,从而提高下游任务的表现(内容和风格)。 另外,由于模式已经被学习到模型,因此,prompt上就可以精减一些,不需要再携带例子,从而节省token,并且也能获得更快的响应。
@poe
现在市面上很多个性化聊天机器人,比如poe.com允许你利用定义prompt来实现个性化,但如果提供足够的对话语料,利用openai的finetune来实现,那将能实现效果更好的个性化机器人,比如现在很火的character.ai(基于LaMDA模型,官方的是经过微调的,普通用户也是通过prompt来定义)。
微调的流程简单分为三步:1)准备并上传数据 2)提交训练并等待训练 3)指定finetune好的模型使用
可基于以下四种模型微调(也可在已经微调过的模型上二次微调):
0)初始准备
安装openai cli(>=0.94)。
pip install --upgrade openai
并且要在openai中获得一个key(这个key需要绑定信用卡才能正常使用),并将其设置为环境变量。
export OPENAI\_API\_KEY="<OPENAI\_API\_KEY>"
1)准备并上传数据
如前面所讲,微调的过程是一个监督学习的过程,是需要标记数据,它不仅要有问题,也要有答案,我们将它“问答对(prompt-competion)”。在openAI中,该数据格式为jsonl,具体参考(https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset),内容如下:
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
...
说明一下,这里的prompt就是我们列举可能的问题以及问法,而completion就是我们的回答内容及格式,最少几百个例子,问答对不能长度有token限制,当然多了更好。有了finetune,那么我们实际推理时,我们就无需很详细的指令和实例,这样就提升了体验。例如,chatGPT立人设的prompt,“你是一个资深的导游...”,这些人设背景可以在finetune中就注入给大模型。 o penAI提供了一个工具可以帮助我们本地校验文件的正确性。
openai tools fine\_tunes.prepare\_data -f <LOCAL\_FILE>
2)提交训练并等待训练
将准备好的文件,放置到某一目录(文件后缀为jsonl),执行下面命令可提交训练。
openai api fine\_tunes.create -t <TRAIN\_FILE\_ID\_OR\_PATH> -m <BASE\_MODEL>
也可以通过api(https://platform.openai.com/docs/api-reference/fine-tunes/create)提交。
训练任务是个异步任务,可以通过这个命令查看任务的状态,对于因为某种原因任务失败了,也可以通过它恢复。待训练完成,它将显示训练好的模型名称。
openai api fine\_tunes.follow -i <YOUR\_FINE\_TUNE\_JOB\_ID>
也可以使用以下命令查看,取消,删除一个任务。
# List all created fine-tunes
openai api fine_tunes.list
# Retrieve the state of a fine-tune. The resulting object includes
# job status (which can be one of pending, running, succeeded, or failed)
# and other information
openai api fine_tunes.get -i <YOUR_FINE_TUNE_JOB_ID>
# Cancel a job
openai api fine_tunes.cancel -i <YOUR_FINE_TUNE_JOB_ID>
3)指定finetune好的模型使用
有多种方法验证调试训练好的模型,如cli,api,playground等。
openai api completions.create -m <FINE\_TUNED\_MODEL> -p <YOUR\_PROMPT>
其中FINE_TUNED_MODEL便是训练好的模型名称。
比较直接的是利用playground查看。
红框下拉框将出现finetune完成的模型,选择它,即可在playground中填写prompt查看返回结果了。
总的来说,得益于良好的产品设计,openai的finetune流程相对比较简单,可以利用它微调自己摘要生成器,情感分类,文章续写等等场景。
网上实例很多,下面是一个实际机场信息问答实例可供体验(训练有费用),进入colab实验:https://colab.research.google.com/github/PradipNichite/Youtube-Tutorials/blob/main/Youtube\_GPT\_3\_Finetuning.ipynb#scrollTo=g3CienaxTTQN
小结
FineTune在大模型中是一个非常重要的领域,但互联网上的信息纷繁复杂,专业性很强,导致很难有一个清晰的脉络,学习成本高昂,希望本文的梳理分析对你有帮助,文章限于篇幅,没有给出太多的案例资料,如果感兴趣,可以关注私聊,我会分享一部分案例,一起探讨摸索。
未完待续...
合集目录:
3)微调