0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ファインチューニングしたモデルでGGUFファイル作成した時に詰まった件について

Posted at

やろうとしたことと起きた問題

microsoft/Phi-3.5-mini-instructをファインチューニングして、それをggufファイルに変換することで、ローカルで推論できるようにしようとした。
しかし、ggufファイルに変換しようとしたところで、
ValueError: Can not map tensor 'model.layers.0.mlp.down_proj.weight.absmax'というエラーが出てしばらく詰まった。
作業環境はgoogle colabである。

結果を一言で

根本的な解決策は見出せなかったが、少し迂回路を取ることでggufファイルを作成しローカルでの推論はできるようになったからひとまずOKとした。

対応手順

具体的には、まず当初は、ファインチューニングした時に元のモデルとRoLAアダプターをマージして保存をしていた。

学習をするときは以下のようにした。

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, TrainingArguments
from peft import get_peft_model, LoraConfig
from trl import SFTTrainer, DataCollatorForCompletionOnlyLM

model_name = "microsoft/Phi-3.5-mini-instruct"

tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.model_max_length = 2048
tokenizer.pad_token = tokenizer.unk_token
tokenizer.pad_token_id = tokenizer.convert_tokens_to_ids(tokenizer.pad_token)
tokenizer.padding_side = 'right'

# 量子化パラメータ
bnb_config = BitsAndBytesConfig(
    load_in_8bit=True,
    bnb_8bit_quant_type="nf4",
    bnb_8bit_compute_dtype=torch.float16,
    bnb_8bit_use_double_quant=False,
)

model = AutoModelForCausalLM.from_pretrained(model_name, quantization_config=bnb_config)
model.config.use_cache = False

# LoraConfigの設定
target_modules = ["o_proj","qkv_proj","gate_up_proj","down_proj","lm_head"]
peft_config = LoraConfig(
    r=8,
    lora_alpha=32,
    lora_dropout=0.1,
    target_modules=target_modules,  # Loraを適用するモジュール (モデル構成の線形層)
    task_type="CAUSAL_LM"
)

# 学習データのミニバッチ構築
response_template = "### 応答:\n" # 私が使ったデータセット特有のテンプレート
collator = DataCollatorForCompletionOnlyLM(tokenizer.encode(response_template, add_special_tokens = False)[2:], tokenizer=tokenizer)

# 学習時の設定
args = TrainingArguments(
    output_dir=f"/content/gdrive/MyDrive/study/models/fine_tuned_phi3.5",
    num_train_epochs=3,
    gradient_accumulation_steps=8,
    per_device_train_batch_size=4,
    save_strategy="no",
    logging_steps=10,
    lr_scheduler_type="constant",
    save_total_limit=1,
    fp16=True,
)


trainer = SFTTrainer(
    model,
    args=args,
    train_dataset=train_dataset,
    formatting_func=formatting_prompts_func,
    max_seq_length=1024,
    data_collator=collator,
    peft_config=peft_config,
)

# 学習の実施
trainer.train()

そして学習結果の保存を以下のように行なった。

trainer.save_model(f"/content/gdrive/MyDrive/study/models/fine_tuned_phi3.5")

ここで注意しなければいけないのは、SFTTrainer.save_modelを使って保存されるのは、RoLAのアダプターのみであり、学習されたモデルそのものは保存されていないという点である。
イメージするなら、元々のモデル(今回の例ならmicrosoft/Phi-3.5-mini-instruct)をRoLAで学習した結果の差分だけ保存されているような感じか。

そこで推論を行える「モデルそのもの」を保存するため、RoLAアダプターと元のモデルをマージして保存した。

# ファインチューニング元のモデルの読み込み
model = AutoModelForCausalLM.from_pretrained("microsoft/Phi-3.5-mini-instruct", device_map="auto", quantization_config=bnb_config)

# ファインチューニングしたRoLAアダプターの読み込み
peft_model_id = f"/content/gdrive/MyDrive/study/models/fine_tuned_phi3.5"
config = PeftConfig.from_pretrained(peft_model_id)
peft_model = PeftModel.from_pretrained(model, peft_model_id)
peft_model = peft_model.to(peft_model.device)

# モデルのマージ
merged_model = peft_model.merge_and_unload()

# マージしたモデルの保存
merged_model.save_pretrained(f"/content/gdrive/MyDrive/study/models/merged_fine_tuned_phi3.5")

次に、このモデルをllama.cppでCPUしかないローカル環境で推論させるためにggufファイルに変換させる必要があった。
その前にcolabでllama.cppをインストールした。

!git clone https://github.com/ggerganov/llama.cpp
%cd llama.cpp
!mkdir build
%cd build
!cmake .. -DLLAMA_CUBLAS=ON
!cmake --build . --config Release
!cp bin/main ..
%cd ..

colabでllama.cppが使えるようになったため、convert_hf_to_gguf.pyでファインチューニングしたモデルをggufにする。

!python /content/llama.cpp/convert_hf_to_gguf.py /content/gdrive/MyDrive/study/models/merged_fine_tuned_phi3.5 --outfile /content/gdrive/MyDrive/study/models/merged_fine_tuned_phi3.5.gguf --outtype f16

ところが実行途中で以下のようなエラーが発生してしまった。

INFO:hf-to-gguf:Loading model: merged_fine_tuned_phi3.5
INFO:gguf.gguf_writer:gguf: This GGUF file is for Little Endian only
INFO:hf-to-gguf:Exporting model...
INFO:hf-to-gguf:rope_factors_long.weight,  torch.float32 --> F32, shape = {48}
INFO:hf-to-gguf:rope_factors_short.weight, torch.float32 --> F32, shape = {48}
INFO:hf-to-gguf:gguf: loading model part 'model.safetensors'
INFO:hf-to-gguf:output.weight,             torch.float16 --> BF16, shape = {3072, 32064}
INFO:hf-to-gguf:token_embd.weight,         torch.float16 --> BF16, shape = {3072, 32064}
INFO:hf-to-gguf:blk.0.attn_norm.weight,    torch.float16 --> F32, shape = {3072}
Traceback (most recent call last):
  File "/content/llama.cpp/convert_hf_to_gguf.py", line 4430, in <module>
    main()
  File "/content/llama.cpp/convert_hf_to_gguf.py", line 4424, in main
    model_instance.write()
  File "/content/llama.cpp/convert_hf_to_gguf.py", line 433, in write
    self.prepare_tensors()
  File "/content/llama.cpp/convert_hf_to_gguf.py", line 297, in prepare_tensors
    for new_name, data_torch in (self.modify_tensors(data_torch, name, bid)):
  File "/content/llama.cpp/convert_hf_to_gguf.py", line 265, in modify_tensors
    return [(self.map_tensor_name(name), data_torch)]
  File "/content/llama.cpp/convert_hf_to_gguf.py", line 213, in map_tensor_name
    raise ValueError(f"Can not map tensor {name!r}")
ValueError: Can not map tensor 'model.layers.0.mlp.down_proj.weight.absmax'

safetensorsの部分でエラーが発生しているので、何かモデルが壊れてしまっているのだろう。
down_projはPhi-3.5の線形層であり、ちょうどRoLAで使っていたレイヤーである。down_proj.weight.absmaxだから、RoLAでdown_proj層の重みの絶対値の最大値を使っているのが悪さをしていそう。
...というところまで当たりをつけて色々調べてみたのだが、直接的な解決策を見つけることができなかった。

仕方がないので、迂回路をとってこのエラーを回避してみた。
具体的には、保存したRoLAアダプターを直接ggufファイルに変換して、それとすでに配布されているmicrosoft/Phi-3.5-mini-instructのggufファイルを組み合わせて推論を行うことに成功した。

元々のmicrosoft/Phi-3.5-mini-instructのモデルを/content/llama.cpp/lora/original_phi3.5に保存しておいて以下を実行。

!python /content/llama.cpp/convert_lora_to_gguf.py --base /content/llama.cpp/lora/original_phi3.5 /content/llama.cpp/lora/fine_tuned_phi3.5/

出力されたログがかなり長いため省略するが、これでうまくggufファイルを作成できた。(名前はFine_Tuned_Phi3.5-F16-LoRA.gguf)

あとはRoLAのggufと元のモデルのggufを組み合わせて推論を行う。
配布されている元のモデルのggufファイル(Phi-3.5-mini-instruct-Q4_K_M.gguf)を適切な場所に配置して以下を実行。

!pip install llama-cpp-python

from llama_cpp import Llama

llm = Llama(
  model_path="/content/gdrive/MyDrive/study/GGUF/Phi-3.5-mini-instruct-Q4_K_M.gguf",  # 元のモデルのgguf
  lora_path="/content/gdrive/MyDrive/study/GGUF/Fine_Tuned_Phi3.5-F16-LoRA.gguf",  # 元のモデルをファインチューニングしたROLAアダプターのgguf
  n_gpu_layers=0, # GPUにオフロードするレイヤーの数。CPUしか使っていないので0
)

text = '[SEP]\n\n### 指示:\n与えられた情景に対して短歌を一つ作成してください。[情景]:「他人には分かっても、自分だけが知らない妻と共に生きる悲しみを感じながら生きる」[/SEP]\n\n### 応答:\n'

output = llm(
  text,
  max_tokens=128,
  echo=True,
)

print(output)

出力のほどは・・・

{'id': 'cmpl-7d0ebb6c-fef4-431b-a582-bf1eee21844b',
 'object': 'text_completion',
 'created': 1728098576,
 'model': '/content/gdrive/MyDrive/study/GGUF/Phi-3.5-mini-instruct-Q4_K_M.gguf',
 'choices': [{'text': 'われ一人知らず悲しき旅を妻も知らず悲しき旅をせしもの',
   'index': 0,
   'logprobs': None,
   'finish_reason': 'stop'}],
 'usage': {'prompt_tokens': 105, 'completion_tokens': 31, 'total_tokens': 136}}

大丈夫そう!

所感

根本的な解決ができなかったのはモヤモヤする。
llama.cpp自体がかなり新しく日々機能が変わったりしているから、この対応がいつまで通用するかも定かではない。
自分の方の原因ではなくライブラリの方の原因だったら、それを特定&修正してプルリクエストをできるような人間になりたいもの・・・。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?