個人程式設計助手: 訓練你自己的編碼助手

HuggingFace發表於2023-11-15

在程式設計和軟體開發這個不斷演變的領域中,對效率和生產力的追求催生了許多卓越的創新。其中一個顯著的創新就是程式碼生成模型的出現,如 CodexStarCoderCode 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

對於這篇部落格,我們選取了基於點贊數排名前十的 Hugging Face 公共倉庫。它們分別是:

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

這是我們用來生成這個資料集的程式碼,而 這是資料集在 Hub 上的連結。下面是它的一個快照:

hf-stack-v1

為了降低專案複雜性,我們沒有考慮對資料集進行去重。如果你對在生產應用中應用去重技術感興趣,這篇部落格文章 是一個極佳的資源,它在程式碼大模型的內容中詳細討論了這個主題。

微調你的個人程式碼助手

在這一部分,我們將展示如何微調以下模型: bigcode/starcoder (15.5B 引數) 、bigcode/starcoderbase-1b (1B 引數) 和 Deci/DeciCoder-1b (1B 引數)。我們將使用一個帶有 40GB 視訊記憶體的 A100 Colab Notebook,並使用 ? PEFT (Parameter-Efficient Fine-Tuning,引數高效微調) 進行所有實驗。此外,我們還將展示如何使用 ? Accelerate 的 FSDP (Fully Sharded Data Parallel,全分片資料並行) 整合,在一臺配備 8 個 80GB 視訊記憶體的 A100 GPU 的機器上完全微調 bigcode/starcoder (15.5B 引數)。訓練目標是 fill in the middle (FIM) ,其中訓練序列的一部分被移動到序列的末尾,並且重排序後的序列被自迴歸地預測。

為什麼選擇 PEFT ?因為全微調代價高昂。讓我們來看一些數字以便更好地理解:

全微調所需的最小 GPU 記憶體:

  1. 引數權重: 2 位元組 (混合精度訓練)
  2. 引數權重梯度: 2 位元組
  3. 使用 Adam 最佳化器時的最佳化器狀態: 4 位元組用於原始 FP32 權重 + 8 位元組用於一階和二階矩估計
  4. 將以上所有內容加在一起的每個引數成本: 每個引數 16 位元組
  5. 15.5B 模型 -> 248GB 的 GPU 記憶體,甚至還沒有考慮儲存中間啟用值所需的巨大記憶體 -> 至少需要 4 個 A100 80GB GPU

由於硬體需求巨大,我們將使用 QLoRA 進行引數高效微調。下面是使用 QLoRA 進行 Starcoder 微調的最小 GPU 記憶體需求:

trainable params: 110,428,160 || all params: 15,627,884,544 || trainable%: 0.7066097761926236

  1. 基礎模型權重: 0.5 位元組 * 15.51B 凍結引數 = 7.755GB
  2. 介面卡 (Adapter) 權重: 2 位元組 * 0.11B 可訓練引數 = 0.22GB
  3. 權重梯度: 2 位元組 * 0.11B 可訓練引數 = 0.22GB
  4. 使用 Adam 最佳化器時的最佳化器狀態: 4 位元組 * 0.11B 可訓練引數 * 3 = 1.32GB
  5. 將以上所有內容加在一起 -> 9.51GB ~ 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 上模型佔用的總記憶體為 26GB批次大小為 4
  2. 對於使用 FSDP 進行全微調,加上 Flash Attention V2 和梯度檢查點,每個 GPU 上佔用的記憶體在 70GB 到 77.6GB 之間, 每個 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 FSDPAccelerate Large Model Training using PyTorch Fully Sharded Data Parallel

資源

  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

我們將探討如何使用 ? PEFT 的 QLoRA 方法對 bigcode/starcoder (15B 引數) 進行微調,使用的硬體是單個 A100 40GB GPU。有關 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

資源

  1. 程式碼庫: 連結。它使用了 Transformers 中最近新增的 Flash Attention V2 支援。
  2. Colab notebook: 連結。請確保選擇帶有 High RAM 設定的 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 評估了單個問題的透過率,考慮了每個問題僅生成一個程式碼候選。我們可以觀察到,在 humaneval-python 上,基礎模型 bigcode/starcoder (15B 引數) 和微調後的 PEFT 模型 smangrul/peft-lora-starcoder15B-v2-personal-copilot-A100-40GB-colab 的效能是可比的。

模型 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 的新加權介面卡 (Adapter) 來降低其權重。

我們將看兩個程式碼填充的例子,其中模型的任務是填充由 <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.ipynbPEFT_StarCoder_Inference.ipynb 中找到。

因此,我們可以觀察到,兩種變體的生成都符合預期。太棒了!?

怎麼在 VS Code 中使用?

你可以輕鬆地使用 ? llm-vscode VS Code 擴充套件配置一個自定義的程式碼補全大模型,並透過 ? Inference EndPoints 託管模型。我們將在下面逐步介紹所需的步驟。你可以在 推理端點文件 中瞭解有關部署端點的更多詳細資訊。

設定推理端點

下面是我們建立自定義推理端點時遵循的步驟的截圖。我們使用了我們的 QLoRA 模型,匯出為一個可以輕鬆載入到 transformers 中的全尺寸的 merged 模型。

ie_1

ie_2

設定 VS Code 擴充套件

只需按照 安裝步驟 操作。在設定中,將下面欄位中的端點替換為你部署的 HF 推理端點的地址。

vs_code_endpoint

使用起來如下所示:

code_completion

微調你自己的程式碼聊天助手

到目前為止,我們訓練的模型特別是作為程式碼完成任務的個人助手培訓。它們沒有被訓練來進行對話或回答問題。 OctocoderStarChat 是這類模型的絕佳示例。本節簡要描述瞭如何實現這一點。

資源

  1. 程式碼庫: 連結。它使用了 Transformers 中最近新增的 Flash Attention V2 支援。
  2. Colab notebook: 連結。請確保選擇帶有 High 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 模型和 LoRAs,以及用於製作你自己的 Dreambooth 模型,你可能會熟悉將不同的 LoRAs 與不同的權重結合起來的概念,使用一個與其訓練基模型不同的 LoRA 模型。在文字/程式碼領域,目前仍是未被探索的領域。我們在這方面進行了實驗,並觀察到了非常有趣的發現。你準備好了嗎?我們出發吧!?

混合匹配 LoRAs

PEFT 目前支援 3 種結合 LoRA 模型的方式,linearsvdcat 。更多細節,請參考 tuners#peft.LoraModel.add_weighted_adapter

我們的 notebook Dance_of_LoRAs.ipynb 提供了所有推理程式碼,並展示了多種 LoRA 模型的載入組合。例如,它展示瞭如何在 starcoder 模型上載入聊天助手介面卡 (Adapter),儘管 starcoderplus 是我們用於微調的基礎模型。

這裡,我們將考慮 2 種能力 ( 聊天/問答程式碼完成 ) 在 2 種資料分佈 ( 前 10 公共 hf 程式碼庫通用程式碼庫 ) 上。這給了我們 4 個軸,我們將在上面進行一些定性評估分析。

首先,讓我們考慮聊天/問答 任務。

如果我們禁用介面卡 (Adapter),我們觀察到對於兩個資料集來說任務都失敗了,因為基模型 ( starcoder ) 僅用於程式碼完成,不適合 聊天/問答 。啟用 copilot 介面卡 (Adapter) 的表現類似於禁用的情況,因為這個 LoRA 也是專門為程式碼完成而微調的。

現在,讓我們啟用 assistant 介面卡 (Adapter)。

assistant_chat_generic

基於生成程式碼的 QA

assistant_chat_hf

基於 HF 程式碼的 QA

我們可以觀察到,關於 scrapy 的通用問題得到了妥善的回答。然而,它未能解答與 HF (Hugging Face) 程式碼相關的問題,因為這不是它預訓練資料的一部分。

現在讓我們考慮 程式碼補全 任務。

在禁用介面卡 (Adapter) 時,我們觀察到對於通用的兩數之和問題,程式碼補全如預期般工作正常。然而,對於 HF 程式碼補全任務,由於基礎模型在其預訓練資料中未曾見過,所以在向 LoraConfig 傳遞引數時出現了錯誤。啟用 assistant 的表現與禁用時相似,因為它是在自然語言對話的基礎上訓練的,這些對話中沒有任何 Hugging Face 程式碼倉庫的內容。

現在,讓我們啟用 copilot 介面卡 (Adapter)。

copilot_code_generic

我們可以觀察到,在兩種情況下 copilot 介面卡 (Adapter) 都得到了正確的結果。因此,無論是在處理 HF (Hugging Face) 特定程式碼庫還是通用程式碼庫時,它都能如預期地完成程式碼補全任務。

現在,作為使用者,我希望能結合 assistantcopilot 的能力。這將使我能夠在 IDE 中編碼時使用它進行程式碼補全,同時也能將它作為聊天機器人來回答我關於 API、類、方法、文件的問題。它應該能夠提供對問題的答案,如 我該如何使用 x ,請在我的程式碼的基礎上 為 Y 編寫一段程式碼片段

PEFT 允許你透過 add_weighted_adapter 來實現這一點。讓我們建立一個新的介面卡 code_buddy ,給予 assistantcopilot 介面卡相同的權重。

combining_loras

結合多種介面卡 (Adapter)

現在,讓我們看看 code_buddy聊天/問答 任務上的表現。

混合聊天 _hf

我們可以觀察到 code_buddy 的表現比單獨的 assistantcopilot 介面卡要好得多!它能夠回答 編寫程式碼片段 的請求,展示如何使用特定的 HF 倉庫 API。然而,它也出現了錯誤連結/解釋的幻覺,這仍然是大型語言模型面臨的一個開放性挑戰。

下面是 code_buddy 在程式碼補全任務上的表現。

混合程式碼通用

我們可以觀察到 code_buddy 的表現與專門為這個任務微調的 copilot 不相上下。

將 LoRA 模型遷移到不同的基礎模型

我們還可以將 LoRA 模型遷移到不同的基礎模型上。
我們將取剛出爐的 Octocoder 模型,並在其上應用我們之前用 starcoder 基礎模型訓練的 LoRA。請檢視以下 notebook PEFT_Personal_Code_CoPilot_Adapter_Transfer_Octocoder.ipynb,瞭解全部程式碼。

程式碼補全任務上的表現

octocoder_code_hf

我們可以觀察到 octocoder 的表現很好。它能夠完成 HF (Hugging Face) 特定的程式碼片段。如 notebook 中所見,它也能夠完成通用的程式碼片段。

聊天/問答任務上的表現

由於 Octocoder 被訓練用來回答有關程式設計的問題和進行對話,讓我們看看它是否能使用我們的 LoRA 介面卡來回答 HF (Hugging Face) 特定的問題。

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 的 Hugging Face 程式碼完成擴充套件配合使用。在我的搭載 M1 Metal GPU 的 Mac 筆記本上,15B 模型執行得非常慢。因此,我們將縮小規模,訓練一個 PEFT LoRA 版本以及一個完全微調版本的 bigcode/starcoderbase-1b 。以下是訓練用的 Colab notebook 連結:

  1. 全微調和 PEFT LoRA 微調 starcoderbase-1b 的 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,因為它是在 Hugging Face 的程式碼上進行訓練的 ? 在回顧了資料收集流程之後,我們對比了使用 QLoRA 和全面微調進行訓練的效果。我們還嘗試了組合不同的 LoRAs,這在文字和程式碼領域是一項尚待開發的技術。在部署方面,我們研究了使用 ? Inference Endpoints 進行遠端推理,並且還展示瞭如何在 VS Code 和 MLC 上本地執行一個較小的模型。

如果你將這些方法應用到了你自己的程式碼庫,請告訴我們!

致謝

我們要感謝 Pedro CuencaLeandro von WerraBenjamin BossanSylvain GuggerLoubna Ben Allal 在撰寫這篇部落格時提供的幫助。


英文原文: https://hf.co/blog/personal-copilot

原文作者: Sourab Mangrulkar, Sayak Paul

譯者: innovation64

審校/排版: zhongdongy (阿東)

相關文章