README 예시 코드에서 LoRA 어댑터가 적용되지 않는 문제

#1
by senior5207 - opened

요약

README 의 Usage 예시를 그대로 실행하면 LoRA 어댑터가 적용되지 않습니다.
PeftModel.from_pretrained() 가 336개 LoRA 키를 모두 매칭하지 못하고, 경고만
출력한 뒤 베이스 모델(google/embeddinggemma-300m) 그대로 추론합니다.

에러가 발생하지 않고 출력 차원도 (2, 768) 로 동일해서, 겉보기에는 정상
동작처럼 보입니다. 실제로는 README 에 기재된 성능(same-work retrieval mAP@R
84.0% 등)과 무관한 베이스 모델 임베딩이 나옵니다.

재현

환경: peft 0.20.0, sentence-transformers 5.6.0, torch 2.12.1

import torch
from peft import PeftModel
from sentence_transformers import SentenceTransformer

base = SentenceTransformer("google/embeddinggemma-300m")
key = "layers.0.self_attn.q_proj.weight"
before = base[0].auto_model.state_dict()[key].clone()

merged = PeftModel.from_pretrained(
    base[0].auto_model, "Baragi-AI/Munche-768"
).merge_and_unload()

after = merged.state_dict()[key]
print("changed:", not torch.equal(before, after))
print("maxdiff:", (before - after).abs().max().item())

출력:

changed: False
maxdiff: 0.0

병합 후에도 가중치가 전혀 변하지 않습니다. 이때 peft 는 다음 경고를 냅니다.

UserWarning: Found missing adapter keys while loading the checkpoint:
['base_model.model.layers.0.self_attn.q_proj.lora_A.default.weight', ...]

원인

adapter_model.safetensors 의 키에 base_model.model. 접두사와 .default
어댑터 이름이 빠져 있습니다.

키 형식
업로드된 어댑터 (peft 0.19.1) layers.0.self_attn.q_proj.lora_A.weight
peft 가 기대하는 형식 base_model.model.layers.0.self_attn.q_proj.lora_A.default.weight

가중치 자체는 정상입니다. 336개 키(24 레이어 × 7 모듈 × 2), rank 32, shape 도
adapter_config.json 및 베이스 모델과 정확히 일치합니다. 순수하게 키 이름
문제입니다.

PeftModel 로 래핑된 상태가 아니라 내부 모듈만 save_pretrained() 된 것으로
보입니다.

adapter_model.safetensors 안의 텐서 키 이름을
layers.0.self_attn.q_proj.lora_A.weight 에서
base_model.model.layers.0.self_attn.q_proj.lora_A.default.weight 형태로 바꾸면
해결됩니다(파일명이 아니라 safetensors 내부의 키 이름입니다). 336개 키 전부에
같은 규칙을 적용하면 되고, 아래 "해결 방법 1" 코드로 일괄 처리할 수 있습니다.

사용자 쪽에서도 "해결 방법 2" 로 우회할 수 있지만, 원제작자분께서 어댑터를
재업로드해주시면 README 예시가 그대로 동작하므로 그편이 좋겠습니다.

해결 방법 1: 원제작자분의 어댑터 재업로드 (권장)

adapter_model.safetensors 를 내려받아 키 이름만 바꾼 뒤 다시 저장합니다.
가중치 값은 건드리지 않습니다.

from huggingface_hub import hf_hub_download
from safetensors.torch import load_file, save_file

path = hf_hub_download("Baragi-AI/Munche-768", "adapter_model.safetensors")
state = load_file(path)

fixed = {}
for key, value in state.items():
    # layers.0.self_attn.q_proj.lora_A.weight
    #   -> base_model.model.layers.0.self_attn.q_proj.lora_A.default.weight
    key = key.replace(".lora_A.weight", ".lora_A.default.weight")
    key = key.replace(".lora_B.weight", ".lora_B.default.weight")
    fixed["base_model.model." + key] = value

assert len(fixed) == len(state) == 336
save_file(fixed, "adapter_model.safetensors")

adapter_config.json 은 수정할 필요가 없습니다. 새로 저장한
adapter_model.safetensors 로 리포지토리 파일을 교체하면 README 의 Usage
예시가 그대로 동작합니다.

교체 후에는 위 "재현" 코드가 다음과 같이 출력되어야 합니다.

changed: True
maxdiff: 0.0234...   # 0 이 아닌 값

해결 방법 2: 사용자 쪽 우회

어댑터가 재업로드되기 전까지는 로컬에서 키를 고쳐 로드할 수 있습니다.
해결 방법 1 과 같은 규칙으로 리네이밍한 어댑터를 로컬 디렉터리에 만든 뒤,
그 경로를 PeftModel.from_pretrained() 에 넘깁니다.

import shutil
from pathlib import Path

from huggingface_hub import hf_hub_download
from peft import PeftModel
from safetensors.torch import load_file, save_file
from sentence_transformers import SentenceTransformer

repo = "Baragi-AI/Munche-768"
local = Path("munche-768-fixed")
local.mkdir(exist_ok=True)

shutil.copy(hf_hub_download(repo, "adapter_config.json"), local)

state = load_file(hf_hub_download(repo, "adapter_model.safetensors"))
fixed = {}
for key, value in state.items():
    key = key.replace(".lora_A.weight", ".lora_A.default.weight")
    key = key.replace(".lora_B.weight", ".lora_B.default.weight")
    fixed["base_model.model." + key] = value
save_file(fixed, local / "adapter_model.safetensors")

model = SentenceTransformer("google/embeddinggemma-300m")
model[0].auto_model = PeftModel.from_pretrained(
    model[0].auto_model, str(local)
).merge_and_unload()

이 방식은 로컬 사본에만 적용되므로, 근본 해결은 해결 방법 1 입니다.

일단 해결방법 2를 써서 작동시키긴 했습니다.
안 되시는 분들은 이렇게 해보세요.

Baragi AI org

상세한 재현과 원인 분석 감사합니다. 말씀해 주신 문제를 확인했으며, 어댑터 파일의 336개 LoRA 키를 표준 PEFT 체크포인트 형식으로 수정해 재업로드했습니다.

Sign up or log in to comment