跳到正文
原文
Hugging Face Blog·· 2023-10-27精选AI 评分62

Personal Copilot:如何训练自己的代码助手

Personal Copilot: Train Your Own Coding Assistant

AI 导读

Hugging Face 博客介绍了 HugCoder 的构建流程,用 top 10 公开仓库代码微调 StarCoder,使其补全自家代码库。

推荐理由

完整给出从代码数据采集到 QLoRA 微调 StarCoder、再接入 VS Code 的全流程与成本对比,可迁移到自有代码库。

正文 · AI 翻译

在编程和软件开发不断演变的格局中,对效率和生产力追求催生了卓越的创新。其中一个创新便是诸如 Codex、StarCoder 和 Code Llama 等代码生成模型的出现。这些模型在生成类似人类编写的代码片段方面展现出了非凡的能力,从而显示了作为编码助手的巨大潜力。

然而,尽管这些预训练模型能在多种任务上表现出色,但地平线上还浮现着一个令人兴奋的可能性:即根据你的具体需求定制代码生成模型的能力。想象一下,可以在企业规模上利用的个性化编码助手。

在这篇博客文章中,我们将展示如何创建 HugCoder 🤗,这是一个基于 huggingface GitHub 组织公开仓库中的代码内容进行微调的代码大语言模型。我们将讨论数据收集工作流程、训练实验以及一些有趣的结果。这将使你能够基于自己的专有代码库创建个人副驾驶。我们还将为你留下几个项目的进一步扩展方向供尝试。

让我们开始 🚀

Using HugCoder in Visual Studio Code to help create a LoRA fine-tune

数据收集工作流程

我们期望的数据集概念上很简单,我们将其结构化如下:

仓库名称 仓库中的文件路径 文件内容
--- --- ---
--- --- ---

使用 Python GitHub API 从GitHub抓取代码内容非常直接。然而,根据仓库数量和仓库内代码文件的数量,很容易遇到API请求频率限制的问题。

为了避免这类问题,我们决定在本地克隆所有公共仓库并从中提取内容,而不是通过API。我们使用了Python的 multiprocessing 模块来并行下载所有仓库,如此下载脚本所示。

一个仓库经常包含非代码文件,如图像、演示文稿和其他资产。我们对此类文件不感兴趣。我们创建了一个扩展名列表来过滤掉它们。对于除Jupyter Notebook之外的其他代码文件,我们简单地使用了“utf-8”编码。对于notebook,我们只考虑了代码单元格。

我们还排除了所有与代码不直接相关的文件路径。这些包括:.git、__pycache__和xcodeproj。

为了使内容的序列化对内存相对友好,我们使用了分块和feather格式。请参考此脚本获取完整实现。

最终数据集可在Hub上获取,其外观如下:

hf-stack-full

在本博客中,我们根据星标数量考虑了HF Mirror上排名前10的公开仓库。它们分别是:

['transformers', 'pytorch-image-models', 'datasets', 'diffusers', 'peft', 'tokenizers', 'accelerate', 'text-generation-inference', 'chat-ui', 'deep-rl-class']

这是我们用来生成此数据集的代码,而这是Hub中的数据。以下是其快照: hf-stack-v1

为了降低项目复杂性,我们没有考虑数据集的去重。如果你有兴趣在生产应用中应用去重技术,这篇博客文章是有关代码大语言模型背景下去重主题的优秀资源。

微调你的个人副驾驶

在本节中,我们将展示如何微调以下模型:bigcode/starcoder(155 亿参数)、bigcode/starcoderbase-1b(10 亿参数)、Deci/DeciCoder-1b(10 亿参数)。我们将在单个 A100 40GB Colab Notebook 上使用 🤗 PEFT(参数高效微调)进行所有实验。此外,我们还将展示如何在配备 8 块 A100 80GB GPU 的机器上,使用 🤗 Accelerate 的 FSDP 集成对 bigcode/starcoder(155 亿参数)进行完全微调。训练目标是中间填充(FIM),即将训练序列的部分内容移至末尾,并以自回归方式预测重新排序后的序列。

为什么使用 PEFT?完全微调成本高昂。让我们用一些数字来说明这一点:

完全微调所需的最低 GPU 显存:

  1. 权重:2 字节(混合精度训练)
  2. 权重梯度:2 字节
  3. 使用 Adam 时的优化器状态:原始 FP32 权重 4 字节 + 一阶和二阶矩估计 8 字节
  4. 将所有上述内容相加,每个参数的成本:每参数 16 字节
  5. 155 亿参数模型 -> 248GB GPU 显存,甚至还未考虑存储中间激活值所需的巨大显存需求 -> 至少需要 4 块 A100 80GB GPU

由于硬件需求巨大,我们将使用 QLoRA 进行参数高效微调。以下是使用 QLoRA 微调 StarCoder 所需的最低 GPU 显存:

可训练参数:110,428,160 || 总参数:15,627,884,544 || 可训练参数占比:0.7066097761926236

  1. 基础模型权重:0.5 字节 * 15.51B 冻结参数 = 7.755 GB
  2. 适配器权重:2 字节 * 0.11B 可训练参数 = 0.22GB
  3. 权重梯度:2 字节 * 0.11B 可训练参数 = 0.12GB
  4. 使用 Adam 时的优化器状态: 4 字节 * 0.11B 可训练参数 * 3 = 1.32GB
  5. 将所有上述内容相加 -> 9.51 GB ~10GB -> 需要 1 块 A100 40GB GPU 🤯。需要 A100 40GB GPU 的原因是,2048 的长序列长度和 4 的训练批量大小导致的中间激活值会带来更高的显存需求。如下文所示,所需 GPU 显存为 26GB,可以在 A100 40GB GPU 上容纳。此外,A100 GPU 与 Flash Attention 2 的兼容性更好。

在上述计算中,我们没有考虑中间激活检查点所需的显存,这部分显存相当巨大。我们利用 Flash Attention V2 和梯度检查点来克服这个问题。

  1. 对于 QLoRA 加上 Flash Attention V2 和梯度检查点,模型在单块 A100 40GB GPU 上占用的总显存为 26 GB,批量大小为 4。
  2. 对于使用 FSDP 加上 Flash Attention V2 和梯度检查点的完全微调,每块 GPU 占用的显存在 70 GB 到 77.6 GB 之间,per_gpu_batch_size 为 1。

请参考 model-memory-usage,轻松计算在 🤗 HF Mirror Hub 上托管的模型上进行训练和执行大模型推理所需的显存大小。

完全微调

我们将了解如何使用 PyTorch 完全分片数据并行(FSDP)技术在 8 块 A100 80GB GPU 上对 bigcode/starcoder(150 亿参数)进行完全微调。有关 FSDP 的更多信息,请参考 使用 PyTorch FSDP 微调 Llama 2 70B 和 使用 PyTorch 完全分片数据并行加速大型模型训练。

资源

  1. 代码库:链接。它使用了 Transformers 中最近添加的 Flash Attention V2 支持。
  2. FSDP 配置:fsdp_config.yaml
  3. 模型:bigcode/stacoder
  4. 数据集:smangrul/hf-stack-v1
  5. 微调模型: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 对 8 块 A100 80GB GPU 每小时 $12.00 的费用,总成本将为$108。

PEFT

我们将探讨如何使用 QLoRA 在单块 A100 40GB GPU 上使用 🤗 PEFT 对 bigcode/starcoder(15B 参数)进行微调。有关 QLoRA 和 PEFT 方法的更多信息,请参阅使用 bitsandbytes、4 位量化和 QLoRA 让 LLM 更加易用以及🤗 PEFT:在低资源硬件上对十亿级模型进行参数高效微调。

资源

  1. 代码库:链接。它使用了 Transformers 中最近添加的 Flash Attention V2 支持。
  2. Colab 笔记本:链接。请确保选择具有高内存设置的 A100 GPU。
  3. 模型:bigcode/stacoder
  4. 数据集:smangrul/hf-stack-v1
  5. QLoRA 微调模型: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 微调的学习率是全量微调的 10 倍。

plots

为了确保我们的 QLoRA 模型不会导致灾难性遗忘,我们在其上运行了 Python Human Eval。以下是我们获得的结果。Pass@1 衡量的是每个问题仅考虑一个生成的代码候选时的完成通过率。我们可以观察到,基础 bigcode/starcoder(15B 参数)与微调后的 PEFT 模型 smangrul/peft-lora-starcoder15B-v2-personal-copilot-A100-40GB-colab 在 humaneval-python 上的性能相当。

模型 Pass@1
bigcode/starcoder 33.57
smangrul/peft-lora-starcoder15B-v2-personal-copilot-A100-40GB-colab 33.37

现在让我们看一些定性样本。在我们的手动分析中,我们注意到 QLoRA 导致了轻微的过拟合,因此我们通过 PEFT 的 add_weighted_adapter 工具创建权重为 0.8 的新加权适配器来降低其权重。

我们将看两个代码填充示例,其中模型的任务是填充由 <FILL_ME> 占位符表示的部分。我们将考虑来自 GitHub Copilot、QLoRA 微调模型和全量微调模型的填充补全。

qualitative_comparison_1 定性示例 1

在上面的示例中,来自 GitHub Copilot 的补全沿着正确的思路,但帮助不大。另一方面,来自 QLoRA 和全量微调模型的补全正确地用必要的参数填充了整个函数调用。然而,它们之后也添加了很多噪声。这可以通过后处理步骤来控制,将补全限制在闭合括号或换行符处。注意,QLoRA 和全量微调模型产生的结果质量相似。

qualitative_comparison_2 定性示例 2

在上面的第二个示例中,GitHub Copilot 没有给出任何补全。这可能是因为 🤗 PEFT 是一个较新的库,尚未包含在 Copilot 的训练数据中,这正是我们试图解决的问题类型。另一方面,来自 QLoRA 和完全微调模型的补全正确地填充了整个函数调用及其必要参数。再次注意,QLoRA 和完全微调模型都生成了相似质量的输出。完全微调模型和 peft 模型的各种示例推理代码分别可在 Full_Finetuned_StarCoder_Inference.ipynb 和 PEFT_StarCoder_Inference.ipynb 中找到。

因此,我们可以观察到,两个变体的生成结果都符合预期。太棒了!🚀

如何在 VS Code 中使用它?

你可以使用 🤗 llm-vscode VS Code 扩展轻松在 VS Code 中配置自定义代码补全 LLM,并通过 🤗 推理端点 托管模型。我们将逐步介绍所需步骤。你可以在推理端点文档中了解有关部署端点的更多详细信息。

设置推理端点

以下是我们在创建自定义推理端点时遵循的步骤截图。我们使用了 QLoRA 模型,导出为完整的合并模型,可以轻松在 transformers 中加载。 ie_1 ie_2

设置 VS Code 扩展

只需按照安装步骤操作。在设置中,替换下方字段中的端点,使其指向你部署的 HF 推理端点。

vs_code_endpoint

使用方式如下所示:

code_completion

微调你自己的代码聊天助手

到目前为止,我们训练的模型专门用于代码补全任务的个人副驾驶。它们并未被训练用于对话或问答。Octocoder 和 StarChat 是此类模型的绝佳示例。本节简要介绍如何实现这一点。

资源

  1. 代码库:链接。它使用了 Transformers 中最近添加的 Flash Attention V2 支持。
  2. Colab 笔记本:链接。请确保选择具有高 RAM 设置的 A100 GPU。
  3. 模型:bigcode/stacoderplus
  4. 数据集:smangrul/code-chat-assistant-v1。混合了 LIMA+GUANACO 并以适当格式准备训练。
  5. 训练后的模型:smangrul/peft-lora-starcoderplus-chat-asst-A100-40GB-colab

LoRA 之舞

如果你曾经尝试使用 Stable Diffusion 模型和 LoRA 来制作自己的 Dreambooth 模型,你可能熟悉将不同 LoRA 与不同权重组合的概念,即使用与训练时不同的基础模型来使用 LoRA 模型。在文本/代码领域,这仍是一片未探索的领域。我们在这方面进行了实验,并观察到了非常有希望的发现。你准备好了吗?让我们开始吧!🚀

混合搭配 LoRA

PEFT 目前支持 3 种组合 LoRA 模型的方式,linear、svd 和 cat。更多详情,请参阅 tuners#peft.LoraModel.add_weighted_adapter。

我们的 notebook Dance_of_LoRAs.ipynb 包含所有推理代码以及各种 LoRA 加载组合,例如将聊天助手加载到 starcoder 之上,而不是 starcodeplus,后者是我们微调所用的基础模型。

这里,我们将针对 2 种数据分布(top 10 public hf codebase 和 generic codebase)考察 2 种能力(chatting/QA 和 code-completion)。这样我们就有了 4 个维度,用于进行一些定性评估分析。

首先,让我们考虑 chatting/QA 任务。

如果我们禁用适配器,会观察到两个数据集上的任务都失败了,因为基础模型(starcoder)仅用于代码补全,不适合 chatting/question-answering。启用 copilot 适配器的表现与禁用情况相似,因为这个 LoRA 也是专门为代码补全微调的。

现在,让我们启用 assistant 适配器。

assistant_chat_generic 基于通用代码的问答

assistant_chat_hf 基于 HF 代码的问答

我们可以观察到,关于 scrapy 的通用问题得到了正确回答。然而,对于其预训练数据中不包含的 HF 代码相关问题则失败了。

现在让我们考虑 code-completion 任务。

禁用适配器后,我们观察到通用两数之和的代码补全按预期工作。然而,HF 代码补全向 LoraConfig 传入了错误的参数,因为基础模型在其预训练数据中从未见过它。启用 assistant 的表现与禁用情况相似,因为它是在自然语言对话上训练的,其中没有任何 HF Mirror 代码仓库。

现在,让我们启用 copilot 适配器。

copilot_code_generic

我们可以观察到,copilot 适配器在两种情况下都能正确处理。因此,在处理 HF 特定代码库以及通用代码库时,它的代码补全表现都符合预期。

现在,作为用户,我希望结合 assistant 和 copilot 的能力。这将使我能够在 IDE 中编码时用它进行代码补全,同时也能将其作为聊天机器人来回答我关于 API、类、方法、文档的问题。它应该能够针对我的代码库回答诸如 How do I use x、Please write a code snippet for Y 之类的问题。

PEFT 允许你通过 add_weighted_adapter 来实现这一点。让我们创建一个新的适配器 code_buddy,其权重与 assistant 和 copilot 适配器相等。

combining_loras 组合多个适配器

现在,让我们看看 code_buddy 在 chatting/question_answering 任务上的表现。

mix_chat_hf

我们可以观察到,code_buddy 的表现远优于单独的 assistant 或 copilot 适配器!它能够回答编写代码片段的请求,展示如何使用特定的 HF 仓库 API。然而,它也会虚构错误的链接/解释,这仍是 LLM 面临的一个开放挑战。

以下是 code_buddy 在代码补全任务上的表现。

mix_code_generic

我们可以观察到,code_buddy 的表现与 copilot 相当,后者是专门为此任务微调的。

将 LoRA 迁移到不同的基础模型

我们还可以将 LoRA 模型迁移到不同的基础模型。我们将采用最新的 Octocoder 模型,并将我们在上面用 starcoder 基础模型训练的 LoRA 应用到它上面。请查看以下 notebook PEFT_Personal_Code_CoPilot_Adapter_Transfer_Octocoder.ipynb 以获取完整代码。

代码补全任务上的表现

octocoder_code_hf

我们可以观察到,octocoder 表现很棒。它能够补全 HF 特定的代码片段。如 notebook 中所示,它也能补全通用代码片段。

聊天/问答任务上的表现

由于 Octocoder 被训练来回答问题和进行有关编程的对话,让我们看看它能否使用我们的 LoRA 适配器来回答 HF 特定的问题。

octocoder_chat_hf

耶!它详细正确地回答了如何创建 LoraConfig 以及相关的 peft 模型,并正确使用了模型名称、数据集名称以及 LoraConfig 的参数值。在禁用适配器的情况下,它无法正确使用 LoraConfig 的 API 或创建 PEFT 模型,这表明这些不是 Octocoder 训练数据的一部分。

我如何在本地运行它?

我知道,在经历了这一切之后,你想要在你的代码库上微调 starcoder,并在你的消费级硬件上本地使用它,比如配备 M1 GPU 的 Mac 笔记本、配备 RTX 4090/3090 GPU 的 Windows ... 别担心,我们已经为你准备好了。

我们将使用这个超酷的开源库 mlc-llm 🔥。具体来说,我们将使用这个分支 pacman100/mlc-llm,它做了一些修改,以便能与 VS Code 的 HF Mirror Code Completion 扩展配合使用。在我的配备 M1 Metal GPU 的 Mac 笔记本上,15B 模型慢得令人痛苦。因此,我们将从小处着手,训练一个 PEFT LoRA 版本以及一个完整微调版本的 bigcode/starcoderbase-1b。训练 colab notebook 链接如下:

  1. 用于 starcoderbase-1b 完整微调和 PEFT LoRA 微调的 Colab notebook:链接

训练损失、评估损失以及学习率调度如下所示:

loss_plots

现在,我们将介绍在本地托管合并后的模型 smangrul/starcoder1B-v2-personal-copilot-merged 并将其与 🤗 llm-vscode VS Code 扩展一起使用的详细步骤。

  1. 克隆仓库
git clone --recursive https://github.com/pacman100/mlc-llm.git && cd mlc-llm/
  1. 安装 mlc-ai 和 mlc-chat(以可编辑模式):
pip install --pre --force-reinstall mlc-ai-nightly mlc-chat-nightly -f https://mlc.ai/wheels
cd python
pip uninstall mlc-chat-nightly
pip install -e "."
  1. 通过以下方式编译模型:
time python3 -m mlc_llm.build --hf-path smangrul/starcoder1B-v2-personal-copilot-merged --target metal  --use-cache=0
  1. 在 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,
+    "temperature": 0.2,
-    "repetition_penalty": 1.0,
    "top_p": 0.95,
-    "mean_gen_len": 128,
+    "mean_gen_len": 64,
-    "max_gen_len": 512,
+    "max_gen_len": 64, 
    "shift_fill_factor": 0.3,
    "tokenizer_files": [
        "tokenizer.json",
        "merges.txt",
        "vocab.json"
    ],
    "model_category": "gpt_bigcode",
    "model_name": "starcoder1B-v2-personal-copilot-merged"
}
  1. 运行本地服务器:
 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
  1. 将 VS Code 中 HF Code Completion 扩展的端点改为指向本地服务器:

local_endpoint

  1. 在 VS Code 中打开一个新文件,粘贴下面的代码,并将光标放在文档引号之间,以便模型尝试填充文档字符串:

local_inference

瞧!⭐️

本文开头的演示就是这个 1B 模型在我 Mac 笔记本上本地运行的。

结论

在这篇博文中,我们了解了如何微调 starcoder 来创建一个了解我们代码的个人副驾驶。我们称它为 🤗 HugCoder,因为我们是在 HF Mirror 代码上训练它的 :) 在了解了数据收集工作流程之后,我们比较了使用 QLoRA 训练与完整微调。我们还尝试了组合不同的 LoRA,这在文本/代码领域仍是一种尚未探索的技术。在部署方面,我们研究了使用 🤗 Inference Endpoints 进行远程推理,还展示了如何使用 VS Code 和 MLC 在设备上执行较小的模型。

如果你将这些方法用于你自己的代码库,请告诉我们!

致谢

我们要感谢 Pedro Cuenca、Leandro von Werra、Benjamin Bossan、Sylvain Gugger 和 Loubna Ben Allal 对撰写这篇博文的帮助。

来源:Hugging Face Blog · hf-mirror.com