LLMを我が物に!!

2023-12-09

はじめに

こんにちは!kotamaです!

最近はChatGPTなどの大規模言語モデル(LLM)の発展がめざましく、自分で文章を考えなくても勝手にAIが書いてくれる世の中になりました!!

でも、ChatGPTなどの既存のLLMに意見とかを述べさせると、中立的で一般的な意見しか述べず、明らかにAIが書いたんだろうな~という文章しか作れません。

そんなわけで、今回は自分の意見に近い文章を出力してくれるLLMを作成しようという試みを書きたいと思います!!

最初に

まずはベースとなるLLMを用意するところから始めます

ChatGPTやBIng AIは高性能なのですが、実際のモデルはダウンロード出来ないので自分好みにカスタマイズできないという欠点があります。

そこでダウンロード可能なChat型LLMであるLlama2をゲットしましょう!!

Llama2はMeta社が開発したオープンモデルでChatGPT3.5にいくつかの指標で勝つなど優秀なモデルです!!

Llama2のダウンロード

Llama2は他のモデルに比べると日本語の返答にあまり強くないという欠点があります。そこで今回はLlama2をベースとして日本語の追加学習を行った「ELYZA-japanese-Llama-2-7b」をベースのモデルとしてダウンロードします

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, PreTrainedTokenizer

model_name = "elyza/ELYZA-japanese-Llama-2-7b-instruct"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype="auto")

tokenizer.save_pretrained("tokenizer")
model.save_pretrained("ELYZA_chat_model")

これで自分用のLLMをダウンロードすることが出来ました!!

自分の好みにカスタマイズする

まず自分用のプロンプトと出力例を用意します

以下のフォーマットで自分用のデータをcsvにします。

system_prompt,timestanp,input_num,input_1,output_1,input_2,output_2,input_3,output_3,input_4,output_4,input_5,output_5,input_6,output_6,input_7,output_7,input_8,output_8,input_9,output_9,input_10,output_10
"",20231208120000,1,"簡潔に自己紹介をして","こんにちは\n僕の名前は樹神宇徳といいます。\n名古屋大学情報学部コンピュータ科学科の2年生です。趣味はプログラミングでよくアプリを作っています。\nよろしくお願いします。",,,,,,,,,

次に訓練用のデータセットを作成します。

NAME = "樹神宇徳"
B_INST, E_INST = "[INST]", "[/INST]"
B_SYS, E_SYS = "<<SYS>>\n", "\n<</SYS>>\n\n"
DEFAULT_SYSTEM_PROMPT = "あなたは{name}という名前の日本人です。今は{time}です。以下の会話に{name}として答えてください"

dataset = pd.Series(["" for _ in range(len(data))])

if torch.backends.mps.is_available():
    mps_device = torch.device("mps")
    model.to(mps_device)

for i in range(len(data)):
    if data.loc[i, "system_prompt"] == "":
        system = B_SYS + DEFAULT_SYSTEM_PROMPT.format(name=NAME, time=data.loc[i, "timestamp"]) + E_SYS
    else:
        system = data.loc[i, "system_prompt"]
    prompt = "{bos_token}{b_inst} {system}{prompt} {e_inst} {output} {eos_token}".format(
        bos_token=tokenizer.bos_token,
        b_inst=B_INST,
        system=system,
        prompt=data.loc[i, "input_1"],
        e_inst=E_INST,
        output=data.loc[i, "output_1"],
        eos_token=tokenizer.eos_token
    )
    for j in range(data.loc[i, "input_num"]-1):
        prompt += "{bos_token}{b_inst} {prompt} {e_inst} {output} {eos_token}".format(
            bos_token=tokenizer.bos_token,
            b_inst=B_INST,
            prompt=data.loc[i, f"input_{j+2}"],
            e_inst=E_INST,
            output=data.loc[i, f"output_{j+2}"]
            eos_token=tokenizer.eos_token
        )

    dataset[i] = prompt

データセットの作成方法は以下のサイトを参考にしてください。

https://huggingface.co/blog/llama2

今回はQLoRAと呼ばれる手法でモデルをファインチューニング(追加学習)させていきます。

QLoRAの理論は難しすぎて理解不能なので、QLoRAの理論を知りたい方は以下のサイトを参考にしてください!

https://arxiv.org/pdf/2305.14314.pdf

訓練コード

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,
)

model = AutoModelForCausalLM.from_pretrained(
    os.path.join(base_dir, model_path),
    quantization_config=quant_config,
    device_map={"": 0}
)
model.config.use_cache = False
model.config.pretraining_tp = 1

tokenizer = AutoTokenizer.from_pretrained(os.path.join(base_dir, tokenizer_path), trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"

peft_params = LoraConfig(
    lora_alpha=16,
    lora_dropout=0.1,
    r=64,
    bias="none",
    task_type="CAUSAL_LM",
)

training_params = TrainingArguments(
    output_dir=os.path.join(base_dir, "trani_output"),
    num_train_epochs=1,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=1,
    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"
)

trainer = SFTTrainer(
    model=model,
    train_dataset=train_data,
    peft_config=peft_params,
    dataset_text_field="text",
    max_seq_length=None,
    tokenizer=tokenizer,
    args=training_params,
    packing=False,
)

trainer.model.save_pretrained(os.path.join(base_dir, "chat_model"))
trainer.tokenizer.save_pretrained(os.path.join(base_dir, "tokenizer"))

参考とした記事は以下の記事です

https://www.datacamp.com/tutorial/fine-tuning-llama-2

終わりに

今回は主にデータ不足で実際に訓練することは出来ませんでしたが、データさえ集めれば自分のコピーLLMを作成することが出来ます!!

是非皆さんも自分のコピーを作ってめんどくさい事を任せましょう!!

おすすめ記事