个人副驾 训练您自己的编码助手
个人副驾实现编码自助培训
在不断发展的编程和软件开发领域中,效率和生产力的追求导致了令人瞩目的创新。其中之一就是代码生成模型的出现,例如Codex、StarCoder和Code Llama。这些模型展示了生成类似人类代码片段的卓越能力,因此在编码助手方面展示了巨大的潜力。
然而,虽然这些预训练模型在各种任务中表现出色,但在不远的未来,还存在一种令人兴奋的可能性:能够根据特定需求定制代码生成模型的能力。想象一下,个性化的编码助手可以在企业范围内发挥作用。
在本博客文章中,我们将展示如何创建HugCoder 🤗,一个基于huggingface
GitHub组织的公共代码库的代码LLM进行微调。我们将讨论数据收集工作流程、训练实验和一些有趣的结果。这将使您能够根据自己的专有代码库创建个人副驾驶员。我们将为您提供一些进一步扩展此项目进行实验的方法。
让我们开始吧 🚀
数据收集工作流程
我们所需的数据集在概念上很简单,我们将其结构化如下:
使用Python GitHub API从GitHub上获取代码内容非常简单。然而,根据存储库的数量和存储库中的代码文件数量,用户可能很容易遇到API速率限制问题。
为了避免这些问题,我们决定在本地克隆所有公共存储库,并从存储库中提取内容,而不是通过API获取。我们使用Python的multiprocessing
模块并行下载所有存储库,如此下载脚本所示。
存储库通常包含非代码文件,如图片、演示文稿和其他资产。我们对这些文件不感兴趣。我们创建了一个扩展名列表来过滤它们。对于非Jupyter Notebook的代码文件,我们简单地使用”utf-8″编码进行解析。对于Notebook,我们仅考虑代码单元格。
我们还排除了与代码无关的所有文件路径,包括:.git
、__pycache__
和xcodeproj
。
为了保持对这些内容的序列化相对内存友好,我们使用分块和feather格式。有关完整实现,请参阅此脚本。
最终的数据集可以在Hub上获得,其外观如下:
对于本博客文章,我们考虑了Hugging Face公共存储库中排名前10的存储库,根据星标进行排名。它们如下所示:
[‘transformers’, ‘pytorch-image-models’, ‘datasets’, ‘diffusers’, ‘peft’, ‘tokenizers’, ‘accelerate’, ‘text-generation-inference’, ‘chat-ui’, ‘deep-rl-class’]
这是我们用来生成此数据集的代码和这是数据集在Hub上的快照。这是它的快照:
为了减少项目复杂性,我们没有考虑数据集的重复项去重。如果你有兴趣在生产应用中应用重复项去重技术,这篇博客文章是一个关于代码LLMs中相关主题的绝佳资源。
优化你自己的个人副驾驶
在本部分中,我们将展示如何优化以下模型:bigcode/starcoder
(15.5B参数)、bigcode/starcoderbase-1b
(1B参数)、Deci/DeciCoder-1b
(1B参数)。我们将使用一台A100 40GB Colab笔记本电脑,使用🤗 PEFT(参数高效优化)进行实验。此外,我们还将展示如何在一台配备8个A100 80GB GPU的机器上对bigcode/starcoder
(15.5B参数)进行全面优化,使用🤗 Accelerate的FSDP集成。训练目标是“填空”,即将训练序列的部分内容移动到末尾,并预测重新排序的序列。
为什么使用PEFT?全面优化是非常昂贵的。让我们来看一些数字来衡量一下:
全面优化所需的最小GPU内存:
- Weight:2个字节(混合精度训练)
- Weight gradient:2个字节
- 使用Adam时的优化器状态:4个字节用于原始FP32权重 + 8个字节用于一阶和二阶动量估计
- 将所有上述参数相加:每个参数16个字节
- 15.5B模型 -> 248GB GPU内存,甚至没有考虑存储中间激活所需的巨大内存需求 -> 至少需要4个A100 80GB GPU
由于硬件需求非常高,我们将使用参数高效优化,使用QLoRA来进行优化。以下是使用QLoRA对StarCoder进行优化的最小GPU内存需求:
可训练参数:110,428,160 || 所有参数:15,627,884,544 || 可训练%:0.7066097761926236
- 基本模型权重:0.5个字节 * 15.51B冻结参数 = 7.755 GB
- Adapter权重:2个字节 * 0.11B可训练参数 = 0.22GB
- Weight gradient:2个字节 * 0.11B可训练参数 = 0.12GB
- 使用Adam时的优化器状态:4个字节 * 0.11B可训练参数 * 3 = 1.32GB
- 将所有上述参数相加 -> 9.51 GB约10GB -> 需要1个A100 40GB GPU 🤯。之所以选择A100 40GB GPU,是因为训练长序列长度为2048、批量大小为4时,中间激活所需的GPU内存较高。正如我们将在下面看到的,所需的GPU内存为26GB,可以容纳在A100 40GB GPU上。此外,A100 GPU与Flash Attention 2的兼容性更好。
在以上计算中,我们没有考虑中间激活检查点所需的内存,这是相当庞大的。我们利用Flash Attention V2和梯度检查点来解决这个问题。
- 对于使用QLoRA与Flash Attention V2和梯度检查点,模型在单个A100 40GB GPU上占用的总内存为26 GB,批量大小为4。
- 对于使用FSDP进行全面优化,同时使用Flash Attention V2和梯度检查点,每个GPU占用的内存范围为70 GB至77.6 GB,每个GPU批量大小为1。
请参考 model-memory-usage 来轻松计算在 🤗 Hugging Face Hub 上托管的模型上训练和执行大型模型推断所需的vRAM数量。
完全微调
我们将看一下如何使用PyTorch Fully Sharded Data Parallel (FSDP) 技术,在8个A100 80GB GPU上对 bigcode/starcoder
(15B 参数) 进行完全微调。有关FSDP的更多信息,请参考 Fine-tuning Llama 2 70B using PyTorch FSDP 和 Accelerate Large Model Training using PyTorch Fully Sharded Data Parallel。
资源
- 代码库: 链接。它使用了最近在Transformers中添加的Flash Attention V2支持。
- FSDP配置: fsdp_config.yaml
- 模型: bigcode/stacoder
- 数据集: smangrul/hf-stack-v1
- 微调后的模型: smangrul/peft-lora-starcoder15B-v2-personal-copilot-A100-40GB-colab
启动训练的命令可在 run_fsdp.sh 中找到。
accelerate launch --config_file "configs/fsdp_config.yaml" train.py \ --model_path "bigcode/starcoder" \ --dataset_name "smangrul/hf-stack-v1" \ --subset "data" \ --data_column "content" \ --split "train" \ --seq_length 2048 \ --max_steps 2000 \ --batch_size 1 \ --gradient_accumulation_steps 2 \ --learning_rate 5e-5 \ --lr_scheduler_type "cosine" \ --weight_decay 0.01 \ --num_warmup_steps 30 \ --eval_freq 100 \ --save_freq 500 \ --log_freq 25 \ --num_workers 4 \ --bf16 \ --no_fp16 \ --output_dir "starcoder-personal-copilot-A100-40GB-colab" \ --fim_rate 0.5 \ --fim_spm_rate 0.5 \ --use_flash_attn
总的训练时间为 9 小时。基于 lambdalabs 提供的8x A100 80GB GPU每小时 $12.00 的费用,总费用将为 $108。
PEFT
我们将看一下如何使用🤗 PEFT在单个A100 40GB GPU上对 bigcode/starcoder
(15B 参数) 进行微调。有关QLoRA和PEFT方法的更多信息,请参考 Making LLMs even more accessible with bitsandbytes, 4-bit quantization and QLoRA 和 🤗 PEFT: Parameter-Efficient Fine-Tuning of Billion-Scale Models on Low-Resource Hardware。
资源
- 代码库:链接。它使用了Transformers中最近添加的Flash Attention V2支持。
- Colab笔记本:链接。确保选择A100 GPU和高内存设置。
- 模型:bigcode/stacoder
- 数据集:smangrul/hf-stack-v1
- QLoRA Fine-tuned 模型:smangrul/peft-lora-starcoder15B-v2-personal-copilot-A100-40GB-colab
启动训练的命令在 run_peft.sh 中给出。总训练时间为 12.5小时。根据 lambdalabs 提供的每小时$1.10,总费用将为$13.75。这非常好 🚀!从成本的角度来看,它比完全微调的成本低7.8倍。
比较
下面的图表显示了 QLoRA 和完全微调的评估损失、训练损失和学习率调度器的对比。我们观察到完全微调导致略低的损失,并且相对于 QLoRA 收敛速度稍快。PEFT Fine-tuning 的学习率是完全微调的10倍。
为了确保我们的 QLoRA 模型不会导致灾难性遗忘,我们对其运行了Python人工评估。以下是我们得到的结果。 Pass@1
衡量的是在每个问题中考虑只有一个生成的代码候选项的通过率。我们可以观察到在基础的 bigcode/starcoder
(15B参数)和经过微调的 PEFT 模型 smangrul/peft-lora-starcoder15B-v2-personal-copilot-A100-40GB-colab
之间,关于 humaneval-python
的性能是相当可比的。
现在让我们来看一些定性的样本。在我们的手动分析中,我们注意到 QLoRA 导致了轻微过拟合,因此我们通过 PEFT 的 add_weighted_adapter
工具创建了新的加权适配器,权重为0.8。
我们将看两个代码填充的例子,模型的任务是填充由 <FILL_ME>
占位符表示的部分。我们将考虑来自GitHub Copilot、QLoRA经过微调的模型和完全微调的模型的填充完成。
定性示例1
在上面的例子中,GitHub Copilot 的填充沿着正确的方向,但没有多大帮助。另一方面,QLoRA和完全微调的模型的填充完成以正确的方式填充了带有必要参数的整个函数调用。然而,它们之后也加入了更多的噪音。可以通过后处理步骤限制填充到闭括号或新行来控制这种情况。请注意,QLoRA和完全微调的模型都能够产生类似质量的结果。
定性示例2
在上述的第二个例子中,GitHub Copilot没有给出任何提示。这可能是因为🤗 PEFT是一个最近的库,尚未包含在Copilot的训练数据中,这正是我们试图解决的问题类型。另一方面,QLoRA和完全微调的模型的自动补全正确地填充了整个函数调用所需的参数。再次注意,QLoRA和完全微调的模型都提供了类似质量的生成结果。完全微调模型和peft模型的推理代码的各种示例可在Full_Finetuned_StarCoder_Inference.ipynb和PEFT_StarCoder_Inference.ipynb中找到。
因此,我们可以观察到这两个变体生成的结果符合预期。太棒了!🚀
如何在VS Code中使用?
您可以使用🤗llm-vscode VS Code扩展轻松配置自定义代码补全LLM,并通过🤗 Inference EndPoints托管模型。我们将逐步介绍所需的步骤。您可以在推理端点文档中了解更多有关部署端点的详细信息。
设置推理端点
以下是我们创建自定义推理端点所遵循的步骤的截图。我们使用了QLoRA模型,并导出为可以轻松加载到transformers
中的完整合并模型。
设置VS Code扩展
只需按照安装步骤即可。在设置中,将字段中的端点替换为指向您部署的HF推理端点的路径。
使用方法如下所示:
到目前为止,我们训练的模型专门用于代码补全任务作为个人助理。它们没有经过训练用于进行对话或回答问题。Octocoder
和StarChat
是此类模型的很好示例。本节简要介绍了如何实现这一点。
资源
- 代码库:链接。它使用了Transformers中最近添加的Flash Attention V2支持。
- Colab笔记本:链接。请确保选择带有高RAM设置的A100 GPU。
- 模型:bigcode/stacoderplus
- 数据集:smangrul/code-chat-assistant-v1。结合了
LIMA+GUANACO
,并以便于训练的格式进行适当格式化。 - 训练完成的模型:smangrul/peft-lora-starcoderplus-chat-asst-A100-40GB-colab
如果您尝试过使用稳定扩散模型和LoRA模型来制作自己的Dreambooth模型,您可能对将不同权重的不同LoRA模型相结合的概念很熟悉,在使用一个与其训练模型不同的基础模型的LoRA模型时,这是未探索过的领域。我们进行了相关的实验,并观察到非常有前途的结果。准备好了吗?我们出发吧!🚀
混合和匹配LoRAs
PEFT目前支持3种合并LoRA模型的方式,即linear
、svd
和cat
。有关更多详细信息,请参阅tuners#peft.LoraModel.add_weighted_adapter。
我们的笔记本Dance_of_LoRAs.ipynb包含了所有的推理代码和各种LoRA加载组合,例如将聊天助手加载到starcoder
而不是我们微调的基础模型starcodeplus
上。
在这里,我们将考虑2种能力(聊天/问答
和代码补全
)在2个数据分布(前10个公共hf代码库
和通用代码库
)上。这给了我们4个轴,我们将在上面进行一些定性评估分析。
首先,让我们考虑聊天/问答
任务。
如果我们禁用适配器,我们发现对于这两个数据集来说,任务都失败了,因为基本模型(starcoder
)只适用于代码补全,而不适合聊天/问答
。启用copilot
适配器的效果与禁用的情况类似,因为这个LoRA模型也是专门为代码补全而微调的。
现在,让我们启用assistant
适配器。
基于通用代码的问答
基于HF代码的问答
我们可以观察到对于关于scrapy
的通用问题,得到了正确的答案。但是,对于与HF代码相关的问题,它在其预训练数据中不存在,所以回答失败了。
现在,让我们来考虑代码补全
任务。
禁用适配器后,我们观察到通用两数之和的代码补全效果如预期。然而,HF代码补全失败了,因为基本模型在其预训练数据中没有见过LoraConfig
这个参数。启用assistant
的效果与禁用的情况类似,因为它是根据自然语言对话进行训练的,没有涉及到Hugging Face的代码仓库。
现在,让我们启用copilot
适配器。
我们可以观察到在这两种情况下,copilot
适配器都能够做出正确的补全。因此,在处理HF特定的代码库和通用的代码库时,它对于代码补全的效果符合预期。
现在,作为用户,我希望能够同时利用assistant
和copilot
的能力。这将使我能够在编码中使用它进行代码补全,并将其作为聊天机器人来回答我关于API、类、方法、文档等方面的问题。它应该能够回答关于“如何使用x”、“请为Y编写一个代码片段”等问题。
PEFT允许您通过add_weighted_adapter
来实现。让我们创建一个新的适配器code_buddy
,它将等权重分配给assistant
和copilot
适配器。
合并多个适配器
现在,让我们看看code_buddy
在chatting/question_answering
任务上的表现。
我们可以观察到,code_buddy
的表现比单独使用assistant
或copilot
适配器要好得多!它能够回答编写代码片段的请求,展示如何使用特定的HF仓库API。然而,它也会产生错误的链接/解释,这仍然是LLM面临的一个挑战。
下面是code_buddy
在代码补全任务上的表现。
我们可以观察到code_buddy
在与为此任务专门微调的copilot
相当的程度上表现出色。
将LoRA适配器转移到不同的基础模型
我们还可以将LoRA适配器转移到不同的基础模型。我们将使用最新发布的Octocoder
模型,并将上面训练的LoRA应用于该模型,基于starcoder
基础模型。请查阅以下notebook:PEFT_Personal_Code_CoPilot_Adapter_Transfer_Octocoder.ipynb查看完整代码。
代码补全任务的性能
我们可以观察到octocoder
的表现非常好。它可以完整地完成HF特定的代码片段。它也可以按照笔记本中所示,完整地完成通用的代码片段。
Chatting/QA任务的性能
由于Octocoder经过训练可以回答关于编码的问题并进行对话,让我们看看它是否可以使用我们的LoRA适配器来回答HF特定的问题。
太棒了!它能够详细地正确回答如何创建LoraConfig
和相关的PEFT模型,正确使用模型名称、数据集名称以及LoraConfig的参数值。当禁用适配器时,它无法正确使用LoraConfig
的API或创建PEFT模型,这表明它不是Octocoder的训练数据的一部分。
我知道,在经历了这一切之后,您想在您的代码库上微调starcoder并在您的消费硬件上本地使用它,比如配备M1 GPU的Mac笔记本、配备RTX 4090/3090 GPU的Windows… 不用担心,我们已经为您做好准备。
我们将使用这个超酷的开源库mlc-llm🔥。具体来说,我们将使用pacman100/mlc-llm这个分支,它有一些改动使其能够与VS Code的Hugging Face Code Completion扩展一起工作。在我配备了M1 Metal GPU的Mac笔记本上,15B的模型非常缓慢。因此,我们将使用较小的模型来训练一个PEFT LoRA版本和bigcode/starcoderbase-1b
的完整微调版本。以下是训练的colab笔记本链接:
- 完全微调和 PEFT LoRA 微调的 Colab 笔记本:
starcoderbase-1b
的链接:链接
下面绘制了训练损失、评估损失以及学习率的变化情况:
现在,我们来看看本地托管合并模型 smangrul/starcoder1B-v2-personal-copilot-merged 的详细步骤,并使用 🤗 llm-vscode VS Code 扩展程序。
- 克隆存储库
git clone --recursive https://github.com/pacman100/mlc-llm.git && cd mlc-llm/
- 安装 mlc-ai 和 mlc-chat(在可编辑模式下):
pip install --pre --force-reinstall mlc-ai-nightly mlc-chat-nightly -f https://mlc.ai/wheelscd pythonpip uninstall mlc-chat-nightlypip install -e "."
- 通过以下命令编译模型:
time python3 -m mlc_llm.build --hf-path smangrul/starcoder1B-v2-personal-copilot-merged --target metal --use-cache=0
- 在
dist/starcoder1B-v2-personal-copilot-merged-q4f16_1/params/mlc-chat-config.json
文件中使用以下值更新配置:
{
"model_lib": "starcoder7B-personal-copilot-merged-q4f16_1",
"local_id": "starcoder7B-personal-copilot-merged-q4f16_1",
"conv_template": "code_gpt",
"temperature": 0.7,
"repetition_penalty": 1.0,
"top_p": 0.95,
"mean_gen_len": 128,
"max_gen_len": 512,
"shift_fill_factor": 0.3,
"tokenizer_files": [
"tokenizer.json",
"merges.txt",
"vocab.json"
],
"model_category": "gpt_bigcode",
"model_name": "starcoder1B-v2-personal-copilot-merged"
}
- 运行本地服务器:
python -m mlc_chat.rest --model dist/starcoder1B-v2-personal-copilot-merged-q4f16_1/params --lib-path dist/starcoder1B-v2-personal-copilot-merged-q4f16_1/starcoder1B-v2-personal-copilot-merged-q4f16_1-metal.so
- 将 HF Code Completion 扩展程序在 VS Code 中的终端点更改为指向本地服务器:
- 在 VS Code 中打开一个新文件,在代码块引号之间粘贴以下代码,并将光标放在文档字符串之间,以使模型尝试填写文档字符串:
就这样!⭐️
此帖子开头的演示是在我 Mac 笔记本电脑上本地运行的 1B 模型。
结论
在这篇博客文章中,我们看到如何微调 starcoder
,创建一个了解我们代码的个人联合驾驶员。我们把它叫做 🤗 HugCoder,因为我们是在 Hugging Face 代码上进行训练的 🙂 在了解数据收集工作流程之后,我们比较了使用 QLoRA 和完全微调进行训练的结果。我们还尝试了将不同的 LoRA 组合在一起,这在文本/代码领域仍然是一种未被开发的技术。对于部署,我们研究了使用 🤗 推理终端进行远程推理,并展示了在 VS Code 和 MLC 上使用较小模型进行设备上的执行。
如果您对自己的代码库使用这些方法,请告诉我们!
致谢
我们要感谢Pedro Cuenca,Leandro von Werra,Benjamin Bossan,Sylvain Gugger和Loubna Ben Allal在撰写本篇博文时提供的帮助。