2024年5月31日金曜日

ツイート情報を LoRA に学習させるためのフォーマットに変換する

ツイート情報を LoRA に学習させるためのフォーマットに変換する

概要

過去に mlx で LoRA 学習をしました今回はツイートのアーカイブデータを学習させられるように加工してみました

環境

  • macOS 11.7.10
  • Python 3.11.6

準備

  • pip install pandas scikit-learn

サンプルコード

基本的な作成の流れは前回と同じです
成果物も json ファイル 3 つになります

ツイートのアーカイブ情報にはいろいろありますが今回は full_text のみ学習させます

命令も単純に「ツイートして」にしていますが本来であれば時刻やそのツイートに関するコンテキストも命令に与えたほうがいいです

import json
from io import StringIO

import pandas as pd
from sklearn.model_selection import train_test_split

# tweets.js を読み込んでツイート情報を辞書に変換
tweets = []
with open("./tweets.js", mode="r") as f:
    for tweet in json.loads(f.read().replace("window.YTD.tweets.part0 = ", "")):
        t = {
            "full_text": tweet["tweet"]["full_text"],
            "created_at": tweet["tweet"]["created_at"],
        }
        tweets.append(t)

# JSONデータをPandas DataFrameに読み込む
df = pd.read_json(StringIO(json.dumps(tweets)))
pd.set_option("display.max_colwidth", 1000)
pd.set_option("display.max_rows", 1000)

# リンクを含むツイートは削除
df = df[~df["full_text"].str.contains("https://")]

print(df.head(100))

# テキスト長が短い学習データTOP1300をデータセットとして扱う
# 学習に時間がかかる場合もしくはメモリを使いすぎる場合は ascending=True にして短い順にするか 1300 の数を小さくすること
df["length"] = df.full_text.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:ツイートして ASSISTANT:{row['full_text']}"}
            file.write(json.dumps(formatted_data, ensure_ascii=False) + "\n")


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

トラブルシューティング

pandas を使う場合は pipenv 配下だとエラー (Segmentation fault) になります
なので pip でインストールしてその配下で使いましょう

最後に

次回はこのデータを使って MLX + LoRA をしてみたいと思います

2024年5月30日木曜日

LoRA 学習用の 512x512 画像を作成する方法

LoRA 学習用の 512x512 画像を作成する方法

概要

流れを紹介します
基本は外部のサービスを使うので将来的にサービスがなくなっている可能性があるので注意してください
その場合は代替サービスを探してください
流れ的には同じことをすれば OK です

環境

  • macOS 11.7.10
  • FireAlpaca 2.11.10

サイズを調整する

まずはざっくり調整します
512 に近づくように拡大 or 縮小します

https://www.waifu2x.net/

512 に変更する

先ほど 512 に近づけたファイルを使って 512 にサイズ指定して変更します
元々 512 に近いファイルであればいきなりこちらでもいいかもしれません

https://www.iloveimg.com/ja/resize-image/resize-png

背景を削除する

人物やキャラクターのみ学習させたい場合は背景情報を削除しましょう

https://www.remove.bg/ja

背景を別の背景にする

自分は FireAlpaca を使いました
ざっくりとした流れは以下です

  1. 背景画像とする素材を準備する
  2. FireAlpaca を開き背景を削除した自分物の画像と背景の素材画像を開く
  3. 新規のファイルを512x512で作成しそれらに新規のレイヤーとして2つの画像を重ねる
  4. それぞれのレイヤーの位置を調整する
  5. 自分物画像などは少し傾けたりするといいかも
  6. 最後にラスタライズして png 画像をエクスポートして終了

最後に

自分なりの LoRA に学習させるための 512x512 サイズの画像を作成する方法を紹介しました

各パーツごとの学習用画像ファイルを作成する場合にはまず各パーツのみの画像を用意しあとは今回の流れに沿えば OK かなと思います

2024年5月28日火曜日

SuperAdapters を使って M2 mac mini 上で日本語モデルの Fine-tuning をしてみる

SuperAdapters を使って M2 mac mini 上で日本語モデルの Fine-tuning をしてみる

概要

SuperAdapters はどのモデルでもどのデータセットでもどのプラットフォームでも Fine-Tuning を実現することができるクロスプラットフォームツールです
今回は M2 mac mini 上で LLaMA の日本語モデルを Fine-tuning してみました

環境

  • macOS 14.4.1
  • SuperAdapters 30ef7d3
  • Python 3.11.8

準備

  • pyenv install 3.11.8

プロジェクト準備

M2 の GPU が使えるようにします

  • git clone https://github.com/cckuailong/SuperAdapters.git
  • cd SuperAdapters
  • pyenv local 3.11.8
  • brew install llvm libomp
  • pip install --pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cpu
  • pip install -r requirements.txt

tokenizers でコンフリクトエラーが出る場合は requirements.txt を以下のように修正します

-tokenizers==0.15.0
+tokenizers==0.19.1

モデルのダウンロード

モデルはこちらを使います
LLaMA ベースの日本語特化モデルになります
fast 版には SuperAdapters が対応していないので普通の instruct 版を使います
モデルサイズは15GB ほどあります

  • brew install git-lfs
  • cd SuperAdapters/LLMs
  • git clone https://huggingface.co/elyza/ELYZA-japanese-Llama-2-7b-instruct

データセットのダウンロード

データセットはこちらを使います

  • cd SuperAdapters/data/train
  • rm example.json
  • git lfs install
  • git clone https://huggingface.co/datasets/bbz662bbz/databricks-dolly-15k-ja-ojousama

学習 Fine-Tuning

  • PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 python finetune.py --model_type llama --data "data/train/databricks-dolly-15k-ja-ojousama" --model_path "LLMs/ELYZA-japanese-Llama-2-7b-instruct" --adapter "lora" --output_dir "output/llama"

W&B アカウントがあるかないか求められるのでない場合は 3 を入力します
学習が完了すると output/llama に作成されます

推論 Inference

  • PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 python inference.py --model_type llama --instruction "こんにちわ" --model_path "LLMs/ELYZA-japanese-Llama-2-7b-instruct" --adapter_weights "output/llama" --max_new_tokens 32

これで Fine-Tuning したモデルの結果が表示されます

トラブルシューティング

PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 に設定していてもメモリサイズが足りない場合強制終了してしまいます
こうなると物理的にメモリを増設するしかないようです

最後に

SuperAdapters を使って M2 mac mini で LLaMA モデルの Fine-Tuning を試してみました
GPU は使ってくれるようですメモリサイズが小さすぎると動作しないようです

参考サイト

2024年5月27日月曜日

M2 mac mini 上に text-generation-webui をインストール

M2 mac mini 上に text-generation-webui をインストール

概要

text-generation-webui は LLM を使ったチャットや追加学習ができる WebUI ツールです
今回は M2 mac mini 上にインストールしてチャットを試してみました

環境

  • macOS 14.4.1
  • text-generation-webui 2024-04-28
  • Python 3.10.10

インストール

  1. https://github.com/oobabooga/text-generation-webui/archive/refs/heads/main.zip をダウンロード
  2. unzip text-generation-webui-main.zip
  3. cd text-generation-webui-main
  4. ./start_macos.sh

起動確認

http://localhost:7860/ にアクセスします
デフォルトはチャットの画面です
まだモデルがないので何もできません

モデルのダウンロード

便利なダウンロードが付属しているのでそれを使います
今回は日本語でチャットしたいので日本語に対応しているモデルをダウンロードしました
モデルのサイズは 5GB ほどあります
llama.cpp が使える形式でないと非常に遅いので gguf に変換されたモデルをダウンロードします

  • ./installer_files/conda/bin/python download-model.py elyza/ELYZA-japanese-Llama-2-7b
  • ./installer_files/conda/bin/python download-model.py mmnga/ELYZA-japanese-Llama-2-7b-fast-instruct-gguf

models/mmnga_ELYZA-japanese-Llama-2-7b-fast-instruct-gguf にダウンロードされます

ELYZA-japanese-Llama-2-7b でも Transformers を使えば動作するのですが GPU がうまく動作しないため llama.cpp を使っています

モデルの設定

Model タブから指定します
リロードしモデルを選択したら Load をクリックして設定します

動作確認

モデルが設定できたら Chat タブに戻って動作を確認します
chat-instruct モードにしましょう (あとから気づきましたが普通の chat でも動作しました)
日本語でも会話ができれば OK です

最後に

M2 mac mini 上に text-generation-webui をインストールしてみました
好きなモデルをダウンロードしてチャットなど試すことができます

次回はせっかくなので LoRA を試してみたいと思います -> Apple Sillicon 上ではまだ動作しないようなので諦めました

参考サイト

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 の無料枠だと一日の使用時間が短いので学習前にリソースが終了する可能性があります

参考サイト