以下の内容はhttps://nikkie-ftnext.hatenablog.com/entry/huggingface-peft-revision-peft-model-or-base-modelより取得しました。


LoRAでPEFTしたモデルをrevision指定して読み込む (huggingface/peft 0.11.1)

タイトルで期待した方には最初にごめんなさいなのですが、0.11.1ではできません
しかしできるようにする変更はマージされており、GitHubからインストールすればできます(0.11.2.dev0)

はじめに

チケットご用意されてほし〜 、nikkieです。

最近のToday I Learnedの一本です。

目次

PEFTモデルを読み込みたい

nikkieはLoRAでファインチューンを完全に理解しました!(馬鹿の山ナウ)

Hugging Faceにアップロードしたモデルはこちら

こちらを読み込んでいきます。
ColabのT4 GPUを使いました

peft 0.11.1で読み込む

revisionを指定しない場合、うまくいきます。
PEFTモデルもそのベースモデルも、mainの最新コミットが指定されたことになると理解しています。

https://huggingface.co/docs/peft/quicktour#inference を見つつも、ファインチューンに使ったnotebookのコードを再利用しています1

import torch
from peft import AutoPeftModelForCausalLM
from transformers import AutoTokenizer, BitsAndBytesConfig

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16,
)
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token

peft_model = AutoPeftModelForCausalLM.from_pretrained(
    "ftnext/gemma-2b-peft-tutorial-english-quotes",
    quantization_config=bnb_config,
    device_map={"": 0},
)

inputs = tokenizer("Quote: Imagination is more", return_tensors="pt").to("cuda:0")
with torch.no_grad():
    outputs = peft_model.generate(**inputs, max_new_tokens=20)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Quote: Imagination is more important than knowledge. Knowledge is limited. Imagination encircles the world.

Author: Albert Einstein

この出力形式、ファインチューンしたモデル(=PEFTモデル)です!

では、revisionを指定すると?

peft_model2 = AutoPeftModelForCausalLM.from_pretrained(
    "ftnext/gemma-2b-peft-tutorial-english-quotes",
    revision="012f11616218df1b57a87eff262c6fa5d035a703",
    quantization_config=bnb_config,
    device_map={"": 0},
)

HTTPError: 404 Client Error: Not Found for url: https://huggingface.co/google/gemma-2b/resolve/012f11616218df1b57a87eff262c6fa5d035a703/config.json

今回の例では、revisionが指すのはLoRAでできたモデルの最新のコミットIDです。
すなわち、revisionを指定しない場合と比べて、PEFTモデルのファイルに違いはありません。

ところが、revisionを指定したときは動きません。
ベースのモデル(gemma-2b)にも同じrevisionを指定したとして扱われており、PEFTモデルとベースモデルとはコミットIDが異なるのでベースモデルがロードできずに落ちます

  • PEFTモデル (ftnext/gemma-2b-peft-tutorial-english-quotes)
    • mainの最新コミット 012f11616218df1b57a87eff262c6fa5d035a703
  • ベースのgoogle/gemma-2b
    • main(そこに 012f11616218df1b57a87eff262c6fa5d035a703 というコミットはない)

peft 0.11.2.dev0にアップデートすればrevisionを指定できる

% pip uninstall -y peft
% pip install git+https://github.com/huggingface/peft
% pip list | grep peft
peft                             0.11.2.dev0

(Colabのセッションを再起動して、必要なセルを実行)

peft_model2 = AutoPeftModelForCausalLM.from_pretrained(
    "ftnext/gemma-2b-peft-tutorial-english-quotes",
    revision="012f11616218df1b57a87eff262c6fa5d035a703",
    quantization_config=bnb_config,
    device_map={"": 0},
)

inputs = tokenizer("Quote: Imagination is more", return_tensors="pt").to("cuda:0")
with torch.no_grad():
    outputs = peft_model2.generate(**inputs, max_new_tokens=20)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

読み込めています🙌

対応の過程を追いかける

issue

対応するpull request

changed name of parameter from revision to base_model_revision for clarity

テストコードを見ました。
https://github.com/huggingface/peft/blob/e02b938e0246e94aa9b4ead39087e2171b94b355/tests/test_hub_features.py#L98-L110

class TestBaseModelRevision:
    def test_load_different_peft_and_base_model_revision(self, tmp_path):
        base_model_id = "hf-internal-testing/tiny-random-BertModel"
        base_model_revision = None
        peft_model_id = "peft-internal-testing/tiny-random-BertModel-lora"
        peft_model_revision = "v1.2.3"

        peft_model = AutoPeftModelForCausalLM.from_pretrained(peft_model_id, revision=peft_model_revision).eval()

        assert peft_model.peft_config["default"].base_model_name_or_path == base_model_id
        assert peft_model.peft_config["default"].revision == base_model_revision

読み込むPEFTモデルはこちらです。

v1.2.3を指定しています https://huggingface.co/peft-internal-testing/tiny-random-BertModel-lora/tree/v1.2.3

ベースにしたモデルはこちら

pull requestの範囲のコードにbreakpointを入れて、以下を理解しました。
https://github.com/huggingface/peft/blob/e02b938e0246e94aa9b4ead39087e2171b94b355/src/peft/auto.py#L58-L137

class _BaseAutoPeftModel:
    @classmethod
    def from_pretrained(
        cls,
        pretrained_model_name_or_path,  # テストコードでは tiny-random-BertModel-lora
        adapter_name: str = "default",
        is_trainable: bool = False,
        config: Optional[PeftConfig] = None,
        revision: Optional[str] = None,  # テストコードでは v1.2.3
        **kwargs,
    ):

        peft_config = PeftConfig.from_pretrained(pretrained_model_name_or_path, revision=revision, **kwargs)
        base_model_path = peft_config.base_model_name_or_path
        base_model_revision = peft_config.revision

        # 略

動きとしては

  1. PEFTモデルの adapter_config.json (など)を取得する(tiny-random-BertModel-lora の v1.2.3 という指定で)
  2. adapter_config.jsonから、ベースモデルやそのrevisionを参照する
    • tiny-random-BertModel
    • revisionはNone
  3. 2を元にベースモデルを読み込む

と理解しました

終わりに

PEFTしたモデルをpeft.AutoPeftModelForCausalLMで読み込むとき、peft 0.11.1までは、revisionを指定するとベースモデルも同じrevisionを指定した扱いとなります(これがエラーの原因)。
次期バージョンのpeftではこれは修正されており、指定したPEFTモデルのadapter_config.jsonを見てベースモデルのrevisionを取得します(PEFTモデルとはもちろん別の値になるので動きます)

今回見た範囲から、0.11.1までは、adapter_config.jsonにベースモデルのrevisionを書いても無視されていたのではないかと思われます。
これも0.11.2.devでは直っていると思います(未検証事項)


  1. ロードしたPEFTモデルにtokenizerもあるようなので、ブラッシュアップの余地がありそうです(宿題事項)



以上の内容はhttps://nikkie-ftnext.hatenablog.com/entry/huggingface-peft-revision-peft-model-or-base-modelより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14