ラベル LLaMA の投稿を表示しています。 すべての投稿を表示
ラベル LLaMA の投稿を表示しています。 すべての投稿を表示

2024年5月25日土曜日

M2 mac mini 上で mlx を使って LoRA Fine-Tuning をしてみる

M2 mac mini 上で mlx を使って LoRA Fine-Tuning をしてみる

概要

前回の Google Colab 上で動作させたコードを mlx 化して M2 mac mini 上で動作させてみました

環境

  • macOS 14.5
  • Python 3.10.12
  • mlx 0.13.1
  • mlx-example 69700d84

準備

  • pyenv install 3.10.12

プロジェクトセットアップ

  • git clone https://github.com/ml-explore/mlx-examples.git
  • cd mlx-examples/lora
  • pyenv local 3.10.12
  • pip install -r requirements.txt

モデルのコンバート

LLaMA ベースの日本語モデル elyza/ELYZA-japanese-Llama-2-7b-instruct を更に safetensors 化したモデルを使います

  • python convert.py --hf-path 4bit/ELYZA-japanese-Llama-2-7b-instruct -q --q-bits 4

コンバートしたモデルは mlx_model 配下に作成されます

  • ls -1 mlx_model
config.json
model.safetensors
model.safetensors.index.json
special_tokens_map.json
tokenizer.json
tokenizer_config.json

データセットのコンバート

データセットのフォーマットが少し独自なので変換する必要があります

ここから databricks-dolly-15k-ja-ojousama.json をダウンロードし ojousama ディレクトリ配下に保存します

こちらのスクリプトを拝借して test.json, valid.json, test.json を作成します

  • pip install pandas scikit-learn
  • vim data_convert.py
import json

import pandas as pd
from sklearn.model_selection import train_test_split

# 元のサンプルデータのファイルパス
input_json_file = "./ojousama/databricks-dolly-15k-ja-ojousama.json"

# JSONデータをPandas DataFrameに読み込む
df = pd.read_json(input_json_file)

# ターゲットをclosed_QAとopen_QAに絞る
df = df[(df.category == "closed_qa") | (df.category == "open_qa")].reset_index(
    drop=True
)

# テキスト長が短い学習データTOP1300をデータセットとして扱う
# 学習に時間がかかる場合もしくはメモリを使いすぎる場合は ascending=True にして短い順にするか 1300 の数を小さくすること
df["length"] = df.instruction.str.len() + df.output.str.len()
df = df.sort_values(by="length", ascending=False)
df = df.head(1300)

# データフレームをシャッフル
df = df.sample(frac=1).reset_index(drop=True)

# validとtest用のデータを100件ずつ取り出し、残りをtrainに分割
valid_df, remaining_df = train_test_split(df, test_size=len(df) - 100, random_state=42)
test_df, train_df = train_test_split(
    remaining_df, test_size=len(remaining_df) - 100, random_state=42
)


# ヘルパー関数:データフレームを新しい形式でJSON Linesファイルに変換
def df_to_jsonl(df, file_name):
    with open(file_name, "w", encoding="utf-8") as file:
        for _, row in df.iterrows():
            formatted_data = {
                "text": f"USER:{row['instruction']} ASSISTANT:{row['output']}"
            }
            file.write(json.dumps(formatted_data, ensure_ascii=False) + "\n")


# 各データセットを対応するJSON Linesファイルに変換
df_to_jsonl(train_df, "./ojousama/train.jsonl")
df_to_jsonl(valid_df, "./ojousama/valid.jsonl")
df_to_jsonl(test_df, "./ojousama/test.jsonl")
  • python data_convert.py

で各種 json ファイルができれば OK です

ls -1 ojousama
databricks-dolly-15k-ja-ojousama.json
test.jsonl
train.jsonl
valid.jsonl

学習させるデータ量について

完結に言うと

  • 文章をたくさん学習させたほうがいいがメモリが足らなくなる
  • 文章が短いと短い文章しか生成してくれなくなるがメモリは少なくて済む

という感じなのでデータセットを作成する際に以下のように文章の長さと文章数を調整すると良いです

# 質問+解答の長さを length というフィールドに登録
df["length"] = df.instruction.str.len() + df.output.str.len()
# 文章が長い順にソート
# df = df.sort_values(by="length", ascending=False)
# 質問+解答の長さが200文字以上400文字以下のみ抽出、この条件で734件で14GBほどメモリ消費
filterd_df = (df["length"] >= 200) & (df["length"] <= 400)
df = df.loc[filterd_df]
# あとはここを調整、1300以下ならそのままで OK
df = df.head(1300)

Fine-Tuning

  • python lora.py --model mlx_model --data ojousama --train --iters 600

iters で学習回数を指定できます
その他学習時のパラメータは python lora.py --help で確認できますが TrainingArguments で指定可能なパラメータすべてを指定できるわけではないようです

結果は以下の通りでだいたい5時間ほどかかりました (学習させるデータを短い版にすると1時間ほどで完了しました)
adapters.npz という numpy ベースのファイルができれば OK です

動作確認: 推論

  • python lora.py --model mlx_model --adapter-file adapters.npz --prompt "USER:日本の首都は? ASSISTANT:"

結果

Loading pretrained model
Total parameters 1055.199M
Trainable parameters 2.097M
Loading datasets
Generating
USER:日本の首都は? ASSISTANT:ワタクシ思いますの、 東京は、日本の首都ですわ 
ξ゚⊿゚)ξ
==========

使用したモデル

使用したデータセット

最後に

M2 mac mini 上で LLaMA ベースの日本語モデルを LoRA Fine-Tuning してみました
スクラッチではないので細かいところまで手は届きませんが性能的にはこれで十分かなと思います
また mlx に対応しておりフルに GPU を使って学習してくれるので Apple Sillicon 上でもそれなりのスピードが出せます
やはり M2 上では mlx が必須のようです

過去にいろいろな webui 系のツールを試してみましたがそれらも mlx に対応してくれると Apple Sillicon 上でも機械学習がやりやすくなるのかなと思いました

参考サイト

2024年5月24日金曜日

LLaMA-Factory を m2 mac mini 上で動作させる方法

LLaMA-Factory を m2 mac mini 上で動作させる方法

概要

LLaMA-Factory は簡単に Fine-Tuning を実現できるツールです
今回は M2 mac mini 上に構築してみました
CLI でも学習できますが今回は WebUI モードを試します

なお Python は 3.8系 を使います

環境

  • macOS 14.4.1
  • LLaMA-Factory 0.7.1
  • Python 3.8.18

準備

  • pyenv install 3.8.18

プロジェクトセットアップ

  • git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
  • cd LLaMA-Factory
  • pyenv local 3.8.18
  • pip install -e '.[torch,metrics]'

WebUI の起動

  • CUDA_VISIBLE_DEVICES=0 GRADIO_SHARE=0 llamafactory-cli webui

これで http://localhost:7860/ で起動します

ちょっと試す

とりあえずデフォルトで準備されているモデルやデータセットを使って Fine-Tuning してみます
最低限の Fine-Tuning に必要な設定は2つだけです

  • 事前学習済みモデルの指定 (huggyllama/llama-7b)
  • データセットの指定 (lima)

llama の事前モデルと llma のデータセットなのであまり効果がない Fine-Tuning になりそうですが今回はテストでとりあえず試したいので良しとします

あとは画面下部にある「Start」をクリックすれば学習が始まります

初回実行時にモデルのダウンロードが入ります
今回のモデル (model-00001-of-00002.safetensors/model-00002-of-00002.safetensors) は 15GB ほどあるのでディスクの容量には注意しましょう

ダウンロードに失敗した場合は結果のディレクトリを一度削除して再度実行すればモデルのダウンロードも再開します

ちなみに今回のモデルとデータセット+パラメータでだいたい 107 x 3 = 321 時間ほど学習に時間がかかりそうです

学習したモデルを試すには Refresh Adapters を押しモデルを指定します
そして Chat タブに移動しプロンプトにメッセージを入力することで Fine-Tuning したモデルを試すことができます

fp16 mixed precision requires a GPU (not ‘mps’) 対策

まず transformers を 4.38.1 にします

  • pip install 'transformers==4.38.1'

https://github.com/hiyouga/LLaMA-Factory/issues/2812

そして Train タブの Compute type を fp32 にします

https://github.com/hiyouga/LLaMA-Factory/issues/2329

更に MPS backend out of memory (MPS allocated: 18.09 GB, other allocations: 384.00 KB, max allowed: 18.13 GB) 対策として PYTORCH_MPS_HIGH_WATERMARK_RATIO を 0.0 にして webui を再起動します

https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/9133

  • PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 CUDA_VISIBLE_DEVICES=0 GRADIO_SHARE=0 llamafactory-cli webui

ちなみにこの設定を行うとメモリを無限に使用するので 16GB しかない M2 mac mini だとスワップが発生しまくります (自分の環境だと約 20GB ほどスワップ領域を使っていました)

最後に

LLaMA-Factory を M2 mac mini 上で動作させてみました
起動まではかなり簡単に行えます
デフォルトだと学習にかなり時間がかかるのでその場合は Max samples = 1Epochs = 1 などの数値を下げて学習してください
ちなみに上記設定であれば 7 分ほどで学習が完了しました (テストには良いと思います

機会があればオリジナルのデータセットを使って Fine-Tuning してみたいと思います

参考サイト

2024年5月23日木曜日

日本語 LLaMA ベースのモデルを Google Colab 上で Fine-Turning してみた

日本語 LLaMA ベースのモデルを Google Colab 上で Fine-Turning してみた

概要

これまでいろいろと M2 mac mini 上で Fine-Tuning を試してきましたがメモリが足りなかったりスペックが低くて学習時間がとんでもなくかかったりと大変でした
なので今回は Google Colab を使ってスクラッチで日本語モデルの Fine-Tuning を試してみます

環境

  • Google Colab (T4 GPU)
    • Python 3

学習コード

コードブロックごとにコメントをつけているのでそれ単位で実行すると良いかなと思います

# 必要なライブラリのインストール
%%capture
%pip install accelerate peft bitsandbytes transformers trl

# 必要なモジュールとクラスのインポート
import os
import torch
from datasets import load_dataset
from transformers import (
    AutoModelForCausalLM,
    AutoTokenizer,
    BitsAndBytesConfig,
    TrainingArguments,
    pipeline,
    logging,
)
from peft import LoraConfig
from trl import SFTTrainer, DataCollatorForCompletionOnlyLM

# 今回使用する日本語言語モデルとデータセット名の定義
base_model = "elyza/ELYZA-japanese-Llama-2-7b-instruct"
dataset = "bbz662bbz/databricks-dolly-15k-ja-ojousama"
new_model = "elyza-2-7b-instruct-ojousama"

# データセットのロード
dataset = load_dataset(dataset, split="train")

# QLoRAを使ってFine-Tuningする、QLoRAにすることで低スペック環境でもFine-Tuningできるようにする
compute_dtype = getattr(torch, "float16")

quant_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=compute_dtype,
    bnb_4bit_use_double_quant=False,
)

# 日本語言語モデルのダウンロードと読み込み、quantization_config を指定して 4bit 量子化 (QLoRA) を設定
model = AutoModelForCausalLM.from_pretrained(
    base_model,
    quantization_config=quant_config,
    device_map={"": 0}
)
model.config.use_cache = False
model.config.pretraining_tp = 1

# モデルからトークナイザーの取得
tokenizer = AutoTokenizer.from_pretrained(base_model, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

# LoRA 用のパラメータ設定
peft_params = LoraConfig(
    lora_alpha=16,
    lora_dropout=0.1,
    r=64,
    bias="none",
    task_type="CAUSAL_LM",
)

# 訓練に必要なハイパーパラメータの設定
training_params = TrainingArguments(
    output_dir="./results",
    num_train_epochs=1,
    # Gradient Accumulation 有効化 (メモリ削減テクニック1)
    per_device_train_batch_size=4,
    gradient_accumulation_steps=16,
    # Gradient Checkpointing の有効化 (メモリ削減テクニック2)
    gradient_checkpointing=True,
    optim="paged_adamw_32bit",
    save_steps=25,
    logging_steps=25,
    learning_rate=2e-4,
    weight_decay=0.001,
    fp16=False,
    bf16=False,
    max_grad_norm=0.3,
    max_steps=-1,
    warmup_ratio=0.03,
    group_by_length=True,
    lr_scheduler_type="constant",
    report_to="tensorboard"
)

# 学習データのフォーマットを行う
# 学習データは json 形式で質問 (instruction)、回答 (output)、入力 (input) などが用意されている
# 今回は質問と回答を使って学習用のデータを準備する
def formatting_prompts_func(example):
    output_texts = []
    for i in range(len(example['instruction'])):
        text = f"### Question: {example['instruction'][i]}\n ### Answer: {example['output'][i]}"
        output_texts.append(text)
    return output_texts

# 学習時に回答部分がどこから始まるか教えてるための区切り文字の準備
response_template = " ### Answer:"
collator = DataCollatorForCompletionOnlyLM(response_template, tokenizer=tokenizer)

# 学習を開始するためのオブジェクト作成
# ここで GPU メモリが上がる
trainer = SFTTrainer(
    model=model,
    train_dataset=dataset,
    peft_config=peft_params,
    formatting_func=formatting_prompts_func,
    data_collator=collator,
    max_seq_length=None,
    tokenizer=tokenizer,
    args=training_params,
    packing=False,
)

# 学習開始
# ここでも GPU メモリが上がるが 10GB 以上は上がらないようにチューニングしているので学習が開始される
trainer.train()

今回のハイパーパラメータで学習はだいたいX時間ほどかかりました

推論コード

prompt = "プログラミングについてはどうお考えですの?"
pipe = pipeline(task="text-generation", model=model, tokenizer=tokenizer, max_length=200)
result = pipe(f"<s>[INST] {prompt} [/INST]")
print(result[0]['generated_text'])

データセット

ベースモデル

Tips: GPU メモリ削減テクニック

Tips: This instance will be ignored in loss calculation. Note, if this happens often, consider increasing the max_seq_length. 対策

SFTTrainer の max_seq_length のデフォルト値は 1024 です
学習させるデータ内に 1024 文字以上ある文章が含まれている場合は少し増やしてもいいかもしれません

Tips: trainer/utils.py:141: UserWarning: Could not find response key

なぜが学習データがうまく読み込めないようです
トークナイザーが原因の可能性もあるようですが response_template などを変更するとうまくいくかもです

参考: https://github.com/huggingface/trl/issues/588

最後に

Google Colab 上で 日本語で事前学習済みの LLaMA ベースのモデルを Fine-Tuning してみました
作成したモデルで推論までできるので低スペックなマシンしかない人でもこの方法ならわりと大規模な言語モデルでも Fine-Tuning できるかなと思います

ただ Google Colab の無料枠だと一日の使用時間が短いので学習前にリソースが終了する可能性があります

参考サイト